diff options
| author | Linnea Gräf <nea@nea.moe> | 2025-05-12 20:33:57 +0200 |
|---|---|---|
| committer | Linnea Gräf <nea@nea.moe> | 2025-05-12 20:33:57 +0200 |
| commit | 4a6ddab6da2bfce1c97a1138ba529f9b9691d998 (patch) | |
| tree | d2eb64f2b12af7a268cfde5685080db00aef3941 /source | |
| download | boobbot-4a6ddab6da2bfce1c97a1138ba529f9b9691d998.tar.gz boobbot-4a6ddab6da2bfce1c97a1138ba529f9b9691d998.tar.bz2 boobbot-4a6ddab6da2bfce1c97a1138ba529f9b9691d998.zip | |
Initial commit
Diffstat (limited to 'source')
| -rw-r--r-- | source/app.d | 18 | ||||
| -rw-r--r-- | source/backronyms.d | 81 | ||||
| -rw-r--r-- | source/misskey.d | 133 |
3 files changed, 232 insertions, 0 deletions
diff --git a/source/app.d b/source/app.d new file mode 100644 index 0000000..b60e192 --- /dev/null +++ b/source/app.d @@ -0,0 +1,18 @@ +import std.stdio; +import backronyms; +import misskey; +import std.process; +import std.logger; +void main() +{ + globalLogLevel = LogLevel.trace; + info("Hello world"); + auto generator = BackronymGenerator.load; + auto misskey = new MisskeyClient( + environment.get("MISSKEY_INSTANCE"), + environment.get("MISSKEY_TOKEN") + ); + + auto boobFormat = "boob is short for\n\n" ~ generator.formatBackronymList("boob"); + writefln("%s", misskey.createNote(new BasicNote("public", boobFormat))); +} diff --git a/source/backronyms.d b/source/backronyms.d new file mode 100644 index 0000000..7b3c9e6 --- /dev/null +++ b/source/backronyms.d @@ -0,0 +1,81 @@ +module backronyms; +import std.stdio; +import std.algorithm; +import std.random; +import std.conv; +import std.range; +import std.typecons; + +struct BackronymGenerator +{ + WordList nouns; + WordList adjectives; + + static BackronymGenerator load() + { + return BackronymGenerator( + WordList.fromFile("SimpleWordlists/Wordlist-Nouns-Common-Audited-Len-3-6.txt"), + WordList.fromFile("SimpleWordlists/Wordlist-Adjectives-All.txt"), + ); + } + + string formatBackronymList(string word) + { + string ret; + for (int i = 0; i < word.length; i++) + { + ret ~= ( + i + 1 == word.length + ? this.nouns : this.adjectives + ).findRandomWord(word[i]); + ret ~= '\n'; + } + return ret; + } +} + +struct WordList +{ + + string[][char] words; + + string[] findBackronym(string word) + { + string[] ret; + + foreach (c; word) + { + ret ~= this.findRandomWord(c); + } + + return ret; + } + + string findRandomWord(char start) + { + return this.words[start].choice; + } + + static WordList fromFile(string filePath) + { + auto file = File(filePath); + return fromLines(file.byLine.map!(to!string)); + } + + static WordList fromLines(R)(R lines) + if (isInputRange!R && is(ElementType!R == string)) + { + WordList ret; + + foreach (line; lines) + { + if (line.length == 0) + continue; + + ret.words[line[0]] ~= line; + } + + return ret; + } + +} diff --git a/source/misskey.d b/source/misskey.d new file mode 100644 index 0000000..823dbe0 --- /dev/null +++ b/source/misskey.d @@ -0,0 +1,133 @@ +module misskey; +import requests; +import std.logger; +import std.format; +import std.conv; +import jsonizer; + +class MisskeyException : Exception +{ + + string path; + Response response; + this( + Response response, + string path, + string msg, + string file = __FILE__, size_t line = __LINE__) + { + super("at path " ~ path ~ ": " ~ msg, file, line); + this.response = response; + this.path = path; + } +} + +class MisskeyClient +{ + string instanceUrl; + string token; + this(string instanceUrl, string token) + { + this.instanceUrl = instanceUrl; + this.token = token; + assert(token.length != 0); + assert(instanceUrl.length != 0); + } + + T request(string method, string path, T, R)(R body) + { + static assert(path[0] == '/'); + auto req = Request(); + req.addHeaders([ + "user-agent": "boobbot", + "authorization": format("Bearer %s", this.token) + ]); + auto fullPath = format("%s/api%s", this.instanceUrl, path); + auto reqBody = toJSONString!R(body); + infof("Executing request to %s with headers %s", fullPath, req.headers); + Response response = mixin("req." ~ method)( + fullPath, + reqBody, + "application/json" + ); + if (response.code / 100 != 2) + throw new MisskeyException(response, path, format("non 200 status code: %s", response + .code)); + return fromJSONString!T(to!string(response.responseBody)); + } + + CreateNoteResponse createNote(BasicNote note) + { + return this.request!("post", "/notes/create", CreateNoteResponse, BasicNote)(note); + } + +} + +class BasicNote +{ + mixin JsonizeMe; + + @jsonize(Jsonize.opt) + { + string visibility; + string text; + string id; + } + + this() + { + } + + this(string visibility, string text) + { + this.visibility = visibility; + this.text = text; + this.id = ""; + } + +} + +class CreateNoteResponse +{ + mixin JsonizeMe; + + @jsonize + BasicNote createdNote; +} + +/* +curl https://sk.amy.rip/api/notes/create \ + --request POST \ + --header 'Content-Type: application/json' \ + --header 'Authorization: Bearer aaaaaaaaaa' \ + --data '{ + "visibility": "public", + "visibleUserIds": [ + "" + ], + "cw": null, + "localOnly": false, + "reactionAcceptance": null, + "noExtractMentions": false, + "noExtractHashtags": false, + "noExtractEmojis": false, + "replyId": null, + "renoteId": null, + "channelId": null, + "text": null, + "fileIds": [ + "" + ], + "mediaIds": [ + "" + ], + "poll": { + "choices": [ + "" + ], + "multiple": true, + "expiresAt": null, + "expiredAfter": null + } +}' +*/ |
