shithub: masto9

ref: 402ed456dc7a1bf16dbcf79e13a566dd57761bf7
dir: /masto9.c/

View raw version
#include <u.h>
#include <libc.h>
#include <stdio.h>
#include <json.h>
#include <auth.h>

#include "masto9.h"

char *URL = "https://fedi.9til.de/api/v1/timelines/home";
char *POSTURL = "https://fedi.9til.de/api/v1/statuses";
char *NOTIFICATIONSURL = "https://fedi.9til.de/api/v1/notifications";

UserPasswd *
getcredentials(char *host)
{
	UserPasswd* p;

	p = auth_getuserpasswd(auth_getkey, "proto=pass service=mastodon server=%s", host);
	if(p == nil)
		sysfatal("mastofs: failed to retrieve token: %r");

	return p;
}


static JSON *
get_json_key(JSON *obj, char *key)
{
	JSON *value = jsonbyname(obj, key);
	if (value == nil)
		sysfatal("jsonbyname: key %s not found in %J", key, obj);
	return value;
}

void
get_timeline(char *token, Toot toots[])
{
	JSON *obj, *id, *content, *reblog_content, *account, *reblog_account, *username, *reblog_username, *display_name, *avatar, *reblog, *media_attachments, *type, *url;
	char *response;
	int i = 0;

	response = httpget(token, URL);

	obj = jsonparse(response);
	if (obj == nil)
		sysfatal("jsonparse: not json");
	if (obj->t != JSONArray)
		sysfatal("jsonparse: not an array");

	for(JSONEl *p = obj->first; p != nil; p = p->next) {
		JSON *toot_json = p->val;

		id = get_json_key(toot_json, "id");
		content = get_json_key(toot_json, "content");
		account = get_json_key(toot_json, "account");
		username = get_json_key(account, "username");
		display_name = get_json_key(account, "display_name");
		avatar = get_json_key(account, "avatar_static");
		reblog = get_json_key(toot_json, "reblog");
		media_attachments = get_json_key(toot_json, "media_attachments");

		Toot toot = emalloc(sizeof(Toot));
		toot.id = strdup((char *)id->s);

		if(reblog->s == nil) {
			toot.reblogged = 0;
			toot.content = strdup((char *)content->s);
		} else {
			reblog_content = get_json_key(reblog, "content");
			reblog_account = get_json_key(reblog, "account");
			reblog_username = get_json_key(reblog_account, "username");

			toot.content = strdup((char *)reblog_content->s);
			toot.reblogged_username = strdup((char *)reblog_username->s);
			toot.reblogged = 1;

			media_attachments = get_json_key(reblog, "media_attachments");
		};
		toot.username = strdup((char *)username->s);
		toot.display_name = strdup((char *)display_name->s);
		toot.avatar_url = strdup((char *)avatar->s);
		toot.attachments_count = 0;

		if(media_attachments->s != nil) {
			int j = 0;
			for(JSONEl *at = media_attachments->first; at != nil; at = at->next) {
				JSON *attachment_json = at->val;
				Attachment attachment = emalloc(sizeof(Attachment));
				type = get_json_key(attachment_json, "type");
				url = get_json_key(attachment_json, "preview_url");
				attachment.type = strdup((char *)type->s);
				attachment.url = strdup((char *)url->s);
				toot.media_attachments[j] = attachment;
				toot.attachments_count++;
				j++;
			}
		}

		toots[i] = toot;
		i++;
	}
	jsonfree(obj);
}

void
get_notifications(char *token, Notification toots[])
{
	JSON *obj, *id, *content, *username, *type, *account, *status;
	char *response;
	int i = 0;

	response = httpget(token, NOTIFICATIONSURL);

  obj = jsonparse(response);
	if (obj == nil)
		sysfatal("jsonparse: not json");
	if (obj->t != JSONArray)
		sysfatal("jsonparse: not an array");

	for(JSONEl *p = obj->first; p != nil; p = p->next) {
		JSON *toot_json = p->val;
    content = emalloc(JSONNull);

		id = get_json_key(toot_json, "id");
		type = get_json_key(toot_json, "type");
    if(strcmp(type->s, "follow") != 0) {
      status = get_json_key(toot_json, "status");
      content = get_json_key(status, "content");
    }
    account = get_json_key(toot_json, "account");
    username = get_json_key(account, "username");

		Notification toot = emalloc(sizeof(Notification));
		toot.id = strdup((char *)id->s);

    toot.type = strdup((char *)type->s);
    toot.content = strdup((char *)content->s);
		toot.username = strdup((char *)username->s);

		toots[i] = toot;
		i++;
	}
	jsonfree(obj);
}

void
post_toot(char *token, char *text)
{
	httppost(token, POSTURL, concat("status=", text));
  print("Posted:\n %s\n", text);
}

void
action_toot(char *token, char *id, char *action)
{
  char *response;
  char url[1024];
  snprintf(url, sizeof(url), "%s/%s/%s", POSTURL, id, action);
  print("URL %s\n", url);
  response = httppost(token, url, "");
  print("Response:\n %s\n", response);
}

void
boost_toot(char *token, char *id)
{
  action_toot(token, id, "reblog");
}

void
unboost_toot(char *token, char *id)
{
  action_toot(token, id, "unreblog");
}

void
fav_toot(char *token, char *id)
{
  action_toot(token, id, "favourite");
}

void
unfav_toot(char *token, char *id)
{
  action_toot(token, id, "unfavourite");
}

void
reply_toot(char *token, char *id, char *text)
{
  char content[TOOTBUFSIZE];
  snprintf(content, TOOTBUFSIZE, "in_reply_to_id=%s&status=%s", id, text);
	httppost(token, POSTURL, content);
}

char *
fmthtml(char *msg)
{
	int wr[2], rd[2], n;
	char buf[TOOTBUFSIZE];

	if(pipe(wr) == -1 || pipe(rd) == -1)
		sysfatal("pipe: %r");
	switch(fork()){
	case -1:
		sysfatal("fork: %r");
		break;
	case 0:
		close(wr[0]);
		close(rd[1]);
		dup(wr[1], 0);
		dup(rd[0], 1);
		execl("/bin/htmlfmt", "htmlfmt -cutf-8 -j", nil);
		sysfatal("exec: %r");
		break;
	default:
		close(wr[1]);
		close(rd[0]);
		write(wr[0], msg, strlen(msg));
		close(wr[0]);
		n = readn(rd[1], buf, sizeof(buf));
		close(rd[1]);
		if(n == -1)
			sysfatal("read: %r\n");
		buf[n] = 0;
		return buf;
	}
	return buf;
}

char *
cleanup(char *str)
{
	remove_tag(str, "<span");
	remove_substring(str, "</span>");

	return str;
}

JSON *
mastodonget(char *token, char *host, char *endpoint)
{
	JSON *obj;
	char *response, url[MAX_URL];

	snprintf(url, MAX_URL, "https://%s/api/v1/%s", host, endpoint);
	response = httpget(token, url);

	obj = jsonparse(response);
	if (obj == nil)
		sysfatal("jsonparse: not json");
	if (obj->t != JSONArray)
		sysfatal("jsonparse: not an array");

	return(obj);
}

void
usage(void)
{
  sysfatal("usage: masto9 url");
}

//echo 'proto=pass service=mastodon server=instanceHostName pass=yourToken user=julienxx' > /mnt/factotum/ctl
void
main(int argc, char**argv)
{
  UserPasswd *p;
  char *token, *server, *command, *text, *id;

  if(argc < 2)
		usage();

  JSONfmtinstall();

  server = argv[1];
  command = argv[2];

  p = getcredentials(server);
	token = p->passwd;

  if(command == nil) {
    get_timeline(token, toots);
    for (int i=0;i<TOOTS_COUNT;i++) {
      Toot toot = toots[i];

      print("\n\n——————————————————————————————————————————————————\n");
      if(toot.reblogged == 1) {
        print("⊙ %s retooted %s:\n", toot.username, toot.reblogged_username);
      } else {
        print("⊙ %s:\n", toot.username);
      }
      print("\n%s", fmthtml(cleanup(toot.content)));
      if(toot.attachments_count>0) {
        for (int j=0;j<toot.attachments_count;j++) {
          Attachment attachment = toot.media_attachments[j];
          print("\n[%s] %s", attachment.type, attachment.url);
        }
        print("\n");
      }
      print("\nReply[%s] | Boost[%s] | Favorite[%s]", toot.id, toot.id, toot.id);
    }
    print("\n");
  } else if(strcmp(command, "toot") == 0) {
    text = argv[3];
    post_toot(token, text);
  } else if(strcmp(command, "fav") == 0) {
    id = argv[3];
    fav_toot(token, id);
  } else if(strcmp(command, "unfav") == 0) {
    id = argv[3];
    unfav_toot(token, id);
  } else if(strcmp(command, "boost") == 0) {
    id = argv[3];
    boost_toot(token, id);
  } else if(strcmp(command, "unboost") == 0) {
    id = argv[3];
    unboost_toot(token, id);
  } else if(strcmp(command, "reply") == 0) {
    id = argv[3];
    reply_toot(token, id, "@sirjofri@mastodon.sdf.org now this should be a proper reply");
  } else if(strcmp(command, "notifications") == 0) {
    get_notifications(token, notifs);

    for (int i=0;i<NOTIFS_COUNT;i++) {
      Notification notif = notifs[i];

      if (strcmp(notif.type, "reblog") == 0) {
        print("⊙ %s retooted\n %s\n", notif.username, fmthtml(cleanup(notif.content)));
      } else if (strcmp(notif.type, "favourite") == 0) {
        print("⊙ %s favorited\n %s\n", notif.username, fmthtml(cleanup(notif.content)));
      } else if (strcmp(notif.type, "mention") == 0) {
        print("⊙ %s mentioned you\n %s\n", notif.username, fmthtml(cleanup(notif.content)));
      } else if (strcmp(notif.type, "follow") == 0) {
        print("⊙ %s followed you\n", notif.username);
      } else if (strcmp(notif.type, "poll") == 0) {
        print("⊙ %s poll ended\n %s\n", notif.username, fmthtml(cleanup(notif.content)));
      }
    }
  }

	exits(nil);
}