{"id":5024,"date":"2025-12-19T23:57:13","date_gmt":"2025-12-19T14:57:13","guid":{"rendered":"https:\/\/matoken.org\/blog\/?p=5024"},"modified":"2025-12-19T23:57:14","modified_gmt":"2025-12-19T14:57:14","slug":"implementing-post-bots","status":"publish","type":"post","link":"https:\/\/matoken.org\/blog\/2025\/12\/19\/implementing-post-bots\/","title":{"rendered":"snac\u306e\u81ea\u52d5\u6295\u7a3f\u6a5f\u80fd"},"content":{"rendered":"<div id=\"preamble\">\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p><span class=\"image\"><img decoding=\"async\" src=\"https:\/\/matoken.org\/blog\/wp-content\/uploads\/2025\/12\/snac-susie.png\" alt=\"snac susie\" width=\"48\"><\/span><\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>\u3055\u304f\u3089\u306e\u30ca\u30ec\u30c3\u30b8\u306b\u8efd\u91cfActivityPub Server \u306esnac \u306b\u3064\u3044\u3066\u306e\u8a18\u4e8b\u3092\u5bc4\u7a3f\u3057\u307e\u3057\u305f\uff0e<\/p>\n<\/div>\n<div class=\"ulist\">\n<ul>\n<li>\n<p><a href=\"https:\/\/matoken.org\/blog\/2025\/12\/09\/sakura-knowledge\/\">\u3055\u304f\u3089\u30a4\u30f3\u30bf\u30fc\u30cd\u30c3\u30c8\u306e\u3055\u304f\u3089\u306e\u30ca\u30ec\u30c3\u30b8\u306bsnac\u306e\u8a18\u4e8b\u3092\u5bc4\u7a3f \u2013 matoken&#8217;s blog<\/a><\/p>\n<\/li>\n<\/ul>\n<\/div>\n<div class=\"paragraph\">\n<p>\u3053\u3053\u306b\u66f8\u304b\u306a\u304b\u3063\u305fsnac \u306e\u3053\u3068\u3092\u3044\u304f\u3064\u304b\u66f8\u3044\u3066\u3044\u3053\u3046\u3068\u601d\u3044\u307e\u3059\uff0e<br \/>\n\u4eca\u65e5\u306fsnac \u306ebot \u5411\u3051\u6295\u7a3f\u6a5f\u80fd\u306b\u3064\u3044\u3066\uff0e<\/p>\n<\/div>\n<p><!--more--><\/p>\n<div class=\"paragraph\">\n<p>bot \u3084\u30a2\u30ca\u30a6\u30f3\u30b9\u306a\u3069\u5411\u3051\u306e\u30a2\u30ab\u30a6\u30f3\u30c8\u3067\u81ea\u52d5\u6295\u7a3f\u3092\u3057\u305f\u3044\u3053\u3068\u304c\u3042\u308a\u307e\u3059\uff0e<br \/>\n\u4f8b\u3048\u3070\u3044\u307e\u8003\u3048\u3066\u3044\u308b\u306e\u306fCalDav \u304b\u3089\u4eca\u65e5\u306e\u30a4\u30d9\u30f3\u30c8\u3092\u6295\u7a3f\u3059\u308b\u30a2\u30ab\u30a6\u30f3\u30c8\uff0e\u73fe\u5728\u306fWebhook \u7d4c\u7531\u3067Discord \u306bpost \u3057\u305f\u308a\u3057\u3066\u3044\u307e\u3059\uff0e<br \/>\nsnac \u3067\u3082\u3044\u304f\u3064\u304b\u81ea\u52d5\u6295\u7a3f\u3059\u308b\u6a5f\u80fd\u304c\u3042\u308a\u307e\u3059\uff0e<\/p>\n<\/div>\n<div class=\"quoteblock\">\n<blockquote>\n<div class=\"paragraph\">\n<p>Implementing post bots<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>snac makes very easy to post messages in a non-interactive manner. This example posts a string:<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>uptime | snac note $SNAC_BASEDIR $SNAC_USER &#8211;<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>You can setup a line like this from a crontab(5) or similar. Take note that you need a) command-line access to the same machine that hosts the snac instance, and b) write permissions to the storage directories and files.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>You can also post non-interactively using the Mastodon API and a command-line http tool like curl(1) or similar. This has the advantage that you can do it remotely from any host, anywhere; the only thing you need is an API Token. This is an example:<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>curl -X POST <a href=\"https:\/\/$SNAC_HOST\/api\/v1\/statuses\" class=\"bare\">https:\/\/$SNAC_HOST\/api\/v1\/statuses<\/a> \\<br \/>\n&#8211;header &#8220;Authorization: Bearer ${TOKEN}&#8221; -d &#8220;status=$(uptime)&#8221;<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>You can obtain an API Token by connecting to the following URL:<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><a href=\"https:\/\/$SNAC_HOST\/oauth\/x-snac-get-token\" class=\"bare\">https:\/\/$SNAC_HOST\/oauth\/x-snac-get-token<\/a><\/p>\n<\/div>\n<\/blockquote>\n<div class=\"attribution\">\n&#8212; man(1) \u3088\u308a\n<\/div>\n<\/div>\n<div class=\"quoteblock\">\n<blockquote>\n<div class=\"literalblock\">\n<div class=\"content\">\n<pre>note basedir uid text [file file ... [-r inReplyTo [-d YYYYmmddHHMMSS]]]\n        Enqueues a Create + Note message to all followers. If the text argument is -e, the external editor defined by  the  EDITOR  environment  variable\n        will  be invoked to prepare a message; if it's - (a lonely hyphen), the post content will be read from stdin.  The rest of command line arguments\n        are treated as media files to be attached to the post. The LANG environment variable (if defined) is used as the post language. An  optional  URL\n        to  a  Fediverse post, prefixed by -r, can be specified for this note to be a reply to. If a date in YYYYmmddHHMMSS format is followed by -d, the\n        note is scheduled for that moment instead of immediately posted (this date must be in the same timezone the user has configured in the web UI).<\/pre>\n<\/div>\n<\/div>\n<div class=\"literalblock\">\n<div class=\"content\">\n<pre>note_unlisted basedir uid text [file file ... [-r inReplyTo [-d YYYYmmddHHMMSS]]]\n        Like the previous one, but creates an \"unlisted\" (or \"quiet public\") post.<\/pre>\n<\/div>\n<\/div>\n<div class=\"literalblock\">\n<div class=\"content\">\n<pre>note_mention basedir uid text [file file ... [-r inReplyTo [-d YYYYmmddHHMMSS]]]\n        Like the previous one, but creates a post only for accounts mentioned in the post body.<\/pre>\n<\/div>\n<\/div>\n<div class=\"literalblock\">\n<div class=\"content\">\n<pre>block basedir instance_url<\/pre>\n<\/div>\n<\/div>\n<\/blockquote>\n<div class=\"attribution\">\n&#8212; man(1) \u3088\u308a\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_snac_note_\u30b3\u30de\u30f3\u30c9\u3067\u306e\u6295\u7a3f\">snac note \u30b3\u30de\u30f3\u30c9\u3067\u306e\u6295\u7a3f<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>snac \u306e\u52d5\u4f5c\u3057\u3066\u3044\u308b\u74b0\u5883\u3067\u5b9f\u884c\u3067\u304d\u308b\u306e\u3067\u3042\u308c\u3070\u304a\u624b\u8efd\u306asnac \u306enote \u30b3\u30de\u30f3\u30c9\u304c\u3042\u308a\u307e\u3059\uff0e<br \/>\nman(1) \u306b\u3042\u308b\u4f8b\u3092\u771f\u4f3c\u3057\u3066\u307f\u307e\u3059\uff0e<\/p>\n<\/div>\n<div class=\"quoteblock\">\n<blockquote>\n<div class=\"paragraph\">\n<p>uptime | snac note $SNAC_BASEDIR $SNAC_USER &#8211;<\/p>\n<\/div>\n<\/blockquote>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre>$ uptime | pee \".\/snac note snac-test test -\" cat\n 23:32:36 up 4 days, 10:17,  1 user,  load average: 1.56, 1.90, 1.69\n23:32:36 [test] enqueue_message http:\/\/127.0.0.1:8001\/test\/p\/1766154756.511450\/Create\n8       \/\ufffd<\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u6295\u7a3f\u3092\u78ba\u8a8d\u3059\u308b\u3068\u3053\u3093\u306a\u611f\u3058\uff0e<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><span class=\"image\"><img decoding=\"async\" src=\"https:\/\/matoken.org\/blog\/wp-content\/uploads\/2025\/12\/snac-note-1.webp\" alt=\"snac note 1\"><\/span><\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>\u65e5\u672c\u8a9e\u3082\u305d\u306e\u307e\u307e\u3067ok \u3067\u3057\u305f\uff0e<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><span class=\"image\"><img decoding=\"async\" src=\"https:\/\/matoken.org\/blog\/wp-content\/uploads\/2025\/12\/snac-note-2.webpp\" alt=\"snac note 2\"><\/span><\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>pfetch \u3092\u6d41\u3057\u8fbc\u3093\u3067\u307f\u308b\u3068\u3053\u3093\u306a\u611f\u3058\u3067\u6574\u5f62\u3092\u8003\u3048\u306a\u3044\u3068\u30c0\u30e1\u305d\u3046\uff0e<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><span class=\"image\"><img decoding=\"async\" src=\"https:\/\/matoken.org\/blog\/wp-content\/uploads\/2025\/12\/snac-note-3.webp\" alt=\"snac note 3\"><\/span><\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_curl_\u3092\u4f7f\u3063\u3066api_\u7d4c\u7531\u3067\u6295\u7a3f\">curl \u3092\u4f7f\u3063\u3066API \u7d4c\u7531\u3067\u6295\u7a3f<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>\u3053\u3061\u3089\u306f\u4e00\u822c\u7684\u306a\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u7d4c\u7531\u3067\u30ea\u30e2\u30fc\u30c8\u3067\u6295\u7a3f\u53ef\u80fd\u306a\u65b9\u6cd5\u3067\u3059\uff0etoken \u3092\u5165\u624b\u3057\u3066curl \u30b3\u30de\u30f3\u30c9\u3067\u6295\u7a3f\u3057\u307e\u3059\uff0e<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>\u307e\u305a\u4ee5\u4e0b\u306e\u3088\u3046\u306aURL \u3067token \u3092\u53d6\u5f97\u3057\u307e\u3059\uff0e<\/p>\n<\/div>\n<div class=\"quoteblock\">\n<blockquote>\n<div class=\"paragraph\">\n<p><a href=\"https:\/\/$SNAC_HOST\/oauth\/x-snac-get-token\" class=\"bare\">https:\/\/$SNAC_HOST\/oauth\/x-snac-get-token<\/a><\/p>\n<\/div>\n<\/blockquote>\n<\/div>\n<div class=\"paragraph\">\n<p>\u4ee5\u4e0b\u306e\u3088\u3046\u306a\u753b\u9762\u306b\u306a\u308b\u306e\u3067\u5bfe\u8c61\u30a2\u30ab\u30a6\u30f3\u30c8\u306e\u60c5\u5831\u3092\u5165\u624b\u3057\u307e\u3059\uff0e<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><span class=\"image\"><img decoding=\"async\" src=\"https:\/\/matoken.org\/blog\/wp-content\/uploads\/2025\/12\/snac-get-token.webp\" alt=\"snac get token\"><\/span><\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>\u4ee5\u4e0b\u306e\u3088\u3046\u306a\u30c8\u30fc\u30af\u30f3\u304c\u53d6\u5f97\u3067\u304d\u308b\u306e\u3067\u30e1\u30e2\u3057\u307e\u3059\uff0e<\/p>\n<\/div>\n<div class=\"quoteblock\">\n<blockquote>\n<div class=\"paragraph\">\n<p>1f3ea50e9ab7cc85f5ff01b8f43fb1ae<\/p>\n<\/div>\n<\/blockquote>\n<\/div>\n<div class=\"paragraph\">\n<p>\u5f8c\u306fcurl \u306a\u3069\u3067\u4ee5\u4e0b\u306e\u3088\u3046\u306bPOST\uff0e<br \/>\njson \u3067\u8fd4\u308a\u5024\u304c\u5e30\u3063\u3066\u304d\u307e\u3059\uff0e<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre>$ curl -X POST http:\/\/127.0.0.1:8001\/api\/v1\/statuses --header \"Authorization: Bearer 1f3ea50e9ab7cc85f5ff01b8f43fb1ae\" -d \"status=$(uptime)\"\n{\n    \"id\": \"1766155742b0f5ed692a273ff5b6ef59b696ddde76\",\n    \"uri\": \"http:\/\/127.0.0.1:8001\/test\/p\/1766155742.788170\",\n    \"url\": \"http:\/\/127.0.0.1:8001\/test\/p\/1766155742.788170\",\n    \"account\": {\n        \"id\": \"488d5ffe15e31aa0be5002853e8006f7\",\n        \"username\": \"test\",\n        \"display_name\": \"test\",\n        \"discoverable\": true,\n        \"group\": false,\n        \"hide_collections\": false,\n        \"indexable\": true,\n        \"noindex\": false,\n        \"roles\": [\n        ],\n        \"acct\": \"test@127.0.0.1:8001\",\n        \"created_at\": \"2025-11-14T19:38:38Z\",\n        \"last_status_at\": \"2025-12-19\",\n        \"bot\": false,\n        \"note\": \"&lt;br&gt;\",\n        \"url\": \"http:\/\/127.0.0.1:8001\/test\",\n        \"uri\": \"http:\/\/127.0.0.1:8001\/test\",\n        \"avatar\": \"http:\/\/127.0.0.1:8001\/susie.png\",\n        \"avatar_static\": \"http:\/\/127.0.0.1:8001\/susie.png\",\n        \"header\": \"\",\n        \"header_static\": \"\",\n        \"emojis\": [\n        ],\n        \"locked\": false,\n        \"followers_count\": 0,\n        \"following_count\": 0,\n        \"statuses_count\": 0,\n        \"fields\": [\n        ]\n    },\n    \"created_at\": \"2025-12-19T14:49:02.000Z\",\n    \"content\": \"23:49:02 up 4 days, 10:34,  1 user,  load average: 1.48, 1.57, 1.68&lt;br&gt;\",\n    \"visibility\": \"public\",\n    \"sensitive\": false,\n    \"spoiler_text\": \"\",\n    \"media_attachments\": [\n    ],\n    \"mentions\": [\n    ],\n    \"tags\": [\n    ],\n    \"emojis\": [\n    ],\n    \"favourites_count\": 0,\n    \"favourited\": false,\n    \"reblogs_count\": 0,\n    \"reblogged\": false,\n    \"replies_count\": 0,\n    \"in_reply_to_id\": null,\n    \"in_reply_to_account_id\": null,\n    \"reblog\": null,\n    \"card\": null,\n    \"language\": \"en\",\n    \"filtered\": [\n    ],\n    \"muted\": false,\n    \"text\": \" 23:49:02 up 4 days, 10:34,  1 user,  load average: 1.48, 1.57, 1.68\",\n    \"edited_at\": null,\n    \"poll\": null,\n    \"bookmarked\": false,\n    \"pinned\": false\n}<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>\u3055\u304f\u3089\u306e\u30ca\u30ec\u30c3\u30b8\u306b\u8efd\u91cfActivityPub Server \u306esnac \u306b\u3064\u3044\u3066\u306e\u8a18\u4e8b\u3092\u5bc4\u7a3f\u3057\u307e\u3057\u305f\uff0e \u3055\u304f\u3089\u30a4\u30f3\u30bf\u30fc\u30cd\u30c3\u30c8\u306e\u3055\u304f\u3089\u306e\u30ca\u30ec\u30c3\u30b8\u306bsnac\u306e\u8a18\u4e8b\u3092\u5bc4\u7a3f \u2013 matoken&#8217;s blog \u3053\u3053\u306b\u66f8 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"webmentions_disabled_pings":false,"webmentions_disabled":false,"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":4,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"federated","footnotes":""},"categories":[7,6,1231],"tags":[493,1243],"class_list":["post-5024","post","type-post","status-publish","format-standard","hentry","category-debian-linux","category-linux","category-trixie","tag-activitypub","tag-snac"],"_links":{"self":[{"href":"https:\/\/matoken.org\/blog\/wp-json\/wp\/v2\/posts\/5024","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/matoken.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/matoken.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/matoken.org\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/matoken.org\/blog\/wp-json\/wp\/v2\/comments?post=5024"}],"version-history":[{"count":1,"href":"https:\/\/matoken.org\/blog\/wp-json\/wp\/v2\/posts\/5024\/revisions"}],"predecessor-version":[{"id":5025,"href":"https:\/\/matoken.org\/blog\/wp-json\/wp\/v2\/posts\/5024\/revisions\/5025"}],"wp:attachment":[{"href":"https:\/\/matoken.org\/blog\/wp-json\/wp\/v2\/media?parent=5024"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/matoken.org\/blog\/wp-json\/wp\/v2\/categories?post=5024"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/matoken.org\/blog\/wp-json\/wp\/v2\/tags?post=5024"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}