ref: c6ef27f226f6a5817532a02c1fb273f1631a564e
dir: /masto9.c/
#include <u.h> #include <libc.h> #include <stdio.h> #include <json.h> #include <auth.h> #include <bio.h> #include "masto9.h" static UserPasswd * getcredentials(char *host) { UserPasswd *p; if((p = auth_getuserpasswd( auth_getkey, "proto=pass service=mastodon server=%s", host)) == nil) sysfatal("getcredentials: failed to retrieve token: %r"); return p; } static JSON * mastodonget(char *token, char *host, char *endpoint) { JSON *obj; char *response, *url; url = esmprint("https://%s/api/v1/%s", host, endpoint); response = httpget(token, url); if((obj = jsonparse(response)) == nil) sysfatal("mastodonget: jsonparse: not json"); return (obj); } static void perform(char *token, char *host, char *id, char *action) { char *url; url = esmprint("https://%s/api/v1/statuses/%s/%s", host, id, action); httppost(token, url, ""); } static char * tootauthor(char *token, char *host, char *id) { JSON *obj, *account, *reblog; char *endpoint, *response; endpoint = esmprint("statuses/%s", id); obj = mastodonget(token, host, endpoint); reblog = getjsonkey(obj, "reblog"); if(reblog->s != nil) { account = getjsonkey(reblog, "account"); } else { account = getjsonkey(obj, "account"); } response = estrdup((char *)getjsonkey(account, "acct")->s); free(account); free(reblog); free(obj); return response; } static void gethome(char *token, char *host, Toot toots[], char *beforeid) { JSON *obj, *id, *content, *reblogcontent, *account, *reblogaccount, *handle, *rebloghandle, *displayname, *avatar, *reblog, *mediaattachments, *type, *previewurl, *remoteurl; char *endpoint; int i = 0; if(beforeid != nil) { endpoint = esmprint("timelines/home?max_id=%s", beforeid); } else { endpoint = "timelines/home"; } obj = mastodonget(token, host, endpoint); if(obj->t != JSONArray) sysfatal("gethome: jsonparse: not an array"); for(JSONEl *p = obj->first; p != nil; p = p->next) { JSON *tootjson = p->val; id = getjsonkey(tootjson, "id"); content = getjsonkey(tootjson, "content"); account = getjsonkey(tootjson, "account"); handle = getjsonkey(account, "acct"); displayname = getjsonkey(account, "display_name"); avatar = getjsonkey(account, "avatar_static"); reblog = getjsonkey(tootjson, "reblog"); mediaattachments = getjsonkey(tootjson, "media_attachments"); Toot *toot = emalloc(sizeof(Toot)); toot->id = estrdup((char *)id->s); if(reblog->s == nil) { toot->reblogged = 0; toot->content = estrdup((char *)content->s); } else { reblogcontent = getjsonkey(reblog, "content"); reblogaccount = getjsonkey(reblog, "account"); rebloghandle = getjsonkey(reblogaccount, "acct"); toot->content = estrdup((char *)reblogcontent->s); toot->rebloggedhandle = estrdup((char *)rebloghandle->s); toot->reblogged = 1; mediaattachments = getjsonkey(reblog, "media_attachments"); }; toot->handle = estrdup((char *)handle->s); toot->displayname = estrdup((char *)displayname->s); toot->avatarurl = estrdup((char *)avatar->s); toot->attachmentscount = 0; if(mediaattachments->s != nil) { int j = 0; for(JSONEl *at = mediaattachments->first; at != nil; at = at->next) { JSON *attachmentjson = at->val; Attachment *attachment = emalloc(sizeof(Attachment)); type = getjsonkey(attachmentjson, "type"); previewurl = getjsonkey(attachmentjson, "preview_url"); remoteurl = getjsonkey(attachmentjson, "remote_url"); attachment->type = estrdup((char *)type->s); if(strcmp(type->s, "image") == 0) { attachment->url = estrdup((char *)previewurl->s); } else { attachment->url = estrdup((char *)remoteurl->s); } toot->mediaattachments[j] = attachment; toot->attachmentscount++; j++; } } toots[i] = *toot; i++; } jsonfree(obj); } static void getnotifications(char *token, char *host, Notification *notifs, char *filter) { JSON *obj, *id, *content, *displayname, *handle, *type, *account, *status, *statusid; char *endpoint; int i = 0; if(strlen(filter) > 0) { endpoint = esmprint("notifications?types[]=%s", filter); } else { endpoint = "notifications"; } obj = mastodonget(token, host, endpoint); if(obj->t != JSONArray) sysfatal("getnotifications: jsonparse: not an array"); for(JSONEl *p = obj->first; p != nil; p = p->next) { JSON *notifjson = p->val; id = getjsonkey(notifjson, "id"); type = getjsonkey(notifjson, "type"); account = getjsonkey(notifjson, "account"); displayname = getjsonkey(account, "display_name"); handle = getjsonkey(account, "acct"); Notification *notif = emalloc(sizeof(Notification)); notif->id = estrdup((char *)id->s); notif->type = estrdup((char *)type->s); notif->displayname = estrdup((char *)displayname->s); notif->handle = estrdup((char *)handle->s); if(strcmp(type->s, "follow") != 0) { status = getjsonkey(notifjson, "status"); content = getjsonkey(status, "content"); statusid = getjsonkey(status, "id"); notif->content = estrdup((char *)content->s); notif->statusid = estrdup((char *)statusid->s); } notifs[i] = *notif; i++; } jsonfree(obj); } static void posttoot(char *token, char *host, char *text) { char *url; url = esmprint("https://%s/api/v1/statuses", host); httppost(token, url, esmprint("status=%s", text)); print("Posted:\n %s\n", text); } static void postattachment(char *token, char *host, char *text, char *filepath) { JSON *obj, *id; char *url, *response, *body; url = esmprint("https://%s/api/v1/media", host); response = upload(token, url, filepath); if((obj = jsonparse(response)) == nil) sysfatal("postattachment: jsonparse: not json"); id = getjsonkey(obj, "id"); url = esmprint("https://%s/api/v1/statuses", host); if(strlen(text) > 0) { body = esmprint("status=%s&media_ids[]=%s", text, id->s); } else { body = esmprint("media_ids[]=%s", id->s); } httppost(token, url, body); print("Posted toot\n"); } static void boost(char *token, char *host, char *id) { perform(token, host, id, "reblog"); print("Boosted toot."); } static void unboost(char *token, char *host, char *id) { perform(token, host, id, "unreblog"); print("Unboosted toot."); } static void fav(char *token, char *host, char *id) { perform(token, host, id, "favourite"); print("Favorited toot."); } static void unfav(char *token, char *host, char *id) { perform(token, host, id, "unfavourite"); print("Unfavorited toot."); } static void reply(char *token, char *host, char *id) { char *content; int fd; char *s, *t, *u, *url; Biobuf body; t = nil; if((fd = open("/dev/consctl", OWRITE | OCEXEC)) >= 0) { write(fd, "holdon", 6); u = tootauthor(token, host, id); print("Reply to %s\n", u); Binit(&body, 0, OREAD); if((s = Brdstr(&body, 0, 1)) != nil) t = esmprint("%s", s); free(s); if(t != nil) { url = esmprint("https://%s/api/v1/statuses", host); content = esmprint("in_reply_to_id=%s&status=@%s %s", id, u, t); httppost(token, url, content); print("\nReply sent.\n"); free(t); } else { fprint(2, "%r\n"); } close(fd); } else { fprint(2, "%r\n"); } } static void debug(char *token, char *host, char *id) { JSON *obj; char *endpoint; endpoint = esmprint("statuses/%s", id); obj = mastodonget(token, host, endpoint); print("%J\n", obj); jsonfree(obj); } static void usage(void) { sysfatal("usage: masto9 DOMAIN [COMMAND] [DATA]"); } void main(int argc, char **argv) { UserPasswd *p; char *token, *host, *cmd, *text, *id, *filepath; if(argc < 2) usage(); JSONfmtinstall(); host = argv[1]; cmd = argv[2]; p = getcredentials(host); token = p->passwd; if(cmd == nil || strcmp(cmd, "home") == 0 || strcmp(cmd, "h") == 0) { Toot toots[TOOTSCOUNT]; gethome(token, host, toots, nil); displaytoots(toots, host); } else if(strcmp(cmd, "toot") == 0 || strcmp(cmd, "t") == 0) { text = argv[3]; posttoot(token, host, text); } else if(strcmp(cmd, "tootwithfile") == 0 || strcmp(cmd, "tf") == 0) { if(argc > 4) { text = argv[3]; filepath = argv[4]; } else { text = ""; filepath = argv[3]; } postattachment(token, host, text, filepath); } else if(strcmp(cmd, "fav") == 0 || strcmp(cmd, "f") == 0) { id = argv[3]; fav(token, host, id); } else if(strcmp(cmd, "unfav") == 0 || strcmp(cmd, "uf") == 0) { id = argv[3]; unfav(token, host, id); } else if(strcmp(cmd, "boost") == 0 || strcmp(cmd, "b") == 0) { id = argv[3]; boost(token, host, id); } else if(strcmp(cmd, "unboost") == 0 || strcmp(cmd, "ub") == 0) { id = argv[3]; unboost(token, host, id); } else if(strcmp(cmd, "reply") == 0 || strcmp(cmd, "r") == 0) { id = argv[3]; reply(token, host, id); } else if(strcmp(cmd, "debug") == 0) { id = argv[3]; debug(token, host, id); } else if(strcmp(cmd, "more") == 0) { id = argv[3]; Toot toots[TOOTSCOUNT]; gethome(token, host, toots, id); displaytoots(toots, host); } else if(strcmp(cmd, "notifications") == 0 || strcmp(cmd, "n") == 0) { Notification notifs[NOTIFSCOUNT]; getnotifications(token, host, notifs, ""); displaynotifications(notifs); } else if(strcmp(cmd, "mentions") == 0 || strcmp(cmd, "m") == 0) { Notification notifs[NOTIFSCOUNT]; getnotifications(token, host, notifs, "mention"); displaynotifications(notifs); } else { print("Unknown command %s.\n", cmd); usage(); } free(p); exits(nil); }