import std/[os, strutils, json, httpclient, times] import libs/tiny_sqlite type User = object id* : int64 username* : string displayName* : string url* : string Image = object id* : int64 url* : string width* : int height* : int Post = object id* : int64 url* : string user* : User createdAt* : DateTime content* : string attachedImages* : seq[Image] proc fetchPosts (client: HttpClient, instance: string, userId: int64, maxId: int64 = 0): seq[Post] = var url : string = "https://" & instance & "/api/v1/accounts/" & $userId & "/statuses" response : string if maxId != 0: url.add("?max_id=" & $maxId) while true: try: response = client.getContent(url) break except HttpRequestError: let msg = getCurrentExceptionMsg() if msg == "429 Too Many Requests": echo "Hit a rate-limit! I'll take a 30 sec break!" sleep(30000) else: break let jsonData = response.parseJson for post in jsonData: var images = newSeq[Image]() for image in post["media_attachments"]: images.add(Image( id : image["id"].getStr.parseInt, url : image["url"].getStr, width : image["meta"]["original"]["width"].getInt, height : image["meta"]["original"]["height"].getInt )) result.add(Post( id : post["id"].getStr.parseInt, url : post["url"].getStr, user : User( id : post["account"]["id"].getStr.parseInt, username : post["account"]["username"].getStr, displayName : post["account"]["display_name"].getStr, url : post["account"]["url"].getStr, ), createdAt : parse(post["created_at"].getStr, "yyyy-MM-dd'T'HH:mm:ss'.'fff'Z'"), content : post["content"].getStr, attachedImages : images )) proc postExists(db: DbConn, id: int64): bool = let query = db.one("SELECT EXISTS(SELECT 1 FROM posts WHERE id=?);", id) if query.isSome: return fromDbValue(query.get[0], bool) else: return false if getEnv("DB_PATH") == "": quit "No DB_PATH given!" let db = openDatabase(getEnv("DB_PATH")) db.execScript(""" CREATE TABLE IF NOT EXISTS posts ( id INTEGER PRIMARY KEY, url TEXT NOT NULL, userId INTEGER NOT NULL, userName TEXT NOT NULL, userDisplay TEXT NOT NULL, userUrl TEXT NOT NULL, createdAt VACHAR(20) NOT NULL, content TEXT ); CREATE TABLE IF NOT EXISTS images ( id INTEGER PRIMARY KEY, post_id INTEGER NOT NULL, url TEXT NOT NULL, width INTEGER NOT NULL, height INTEGER NOT NULL, containsHorn BOOL NOT NULL );""") let client : HttpClient = newHttpClient() instance : string = if getEnv("INSTANCE") != "": getEnv("INSTANCE") else: "chaos.social" userId : int64 = if getEnv("USER_ID") != "": getEnv("USER_ID").parseInt else: 000069675 oldestPostId : int64 = if getEnv("OLDEST_POST") != "": getEnv("OLDEST_POST").parseBiggestInt else: 101063382828117630 allPostsFetched : bool = db.postExists(oldestPostId) var posts : seq[Post] = client.fetchPosts(instance, userId) lastPostId : int64 = 0 block fetchPosts: while true: for post in posts: lastPostId = post.id if not db.postExists(post.id): db.exec(""" INSERT INTO posts(id, url, userId, userName, userDisplay, userUrl, createdAt, content) VALUES(?, ?, ?, ?, ?, ?, ?, ?); """, post.id, post.url, post.user.id, post.user.username, post.user.displayName, post.user.url, post.createdAt.format("yyyy-MM-dd'T'HH:mm:ss"), post.content) for image in post.attachedImages: db.exec(""" INSERT INTO images(id, post_id, url, width, height, containsHorn) VALUES(?, ?, ?, ?, ?, ?); """, image.id, post.id, image.url, image.width, image.height, true) echo "Inserted new post with id: " & $post.id if post.id == oldestPostId: echo "All posts successfully fetched!" break fetchPosts else: if allPostsFetched: echo "All new posts fetched!" break fetchPosts else: echo "Post exists!" posts = client.fetchPosts(instance, userId, lastPostId) db.close()