From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp0.migadu.com ([2001:41d0:403:58f0::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms1.migadu.com with LMTPS id 4CSKDCKuG2aoHwAAqHPOHw:P1 (envelope-from ) for ; Sun, 14 Apr 2024 12:21:22 +0200 Received: from aspmx1.migadu.com ([2001:41d0:403:58f0::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp0.migadu.com with LMTPS id 4CSKDCKuG2aoHwAAqHPOHw (envelope-from ) for ; Sun, 14 Apr 2024 12:21:22 +0200 X-Envelope-To: larch@yhetil.org Authentication-Results: aspmx1.migadu.com; dkim=pass header.d=online.de header.s=s42582890 header.b=Q0kLDM8E; dmarc=pass (policy=none) header.from=online.de; spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org" ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1713090081; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:mime-version:mime-version: content-type:content-type:list-id:list-help:list-unsubscribe: list-subscribe:list-post:dkim-signature; bh=Ht7aMNxHpHU+4VDHPSoeLDWCDaU1Ht1LtGPVSvZnnd8=; b=A5wAl7vdy6qeCm858aDTZIfqaVXN5SnMEfAjMZWw7VgfXL+11txw3utTnorh7BYiDvsoa2 Q6bCqgEtt1pP7hnk3lVDLKPZs1OuswYiGm/xUp4RdFBLNEBZeeYj9ORQ0V0b+Ha9wGXx6m SdVwB9yIqJJNLOVfkR+rsIdhdgeRr/KTTyV6Czc8NR38cfmFS5mBsNGFEAblKsD7CHqWGC COS8LBn/jZPhBFpbcrMFfCLqpLHVszgXgfpzgk/J4UJm4LMTCdtXaW7CcU62kE/zGc0xLh Rqmc8wdjGaTz+Z/3SDHEMjTI8ZtIl+4aeeBZCDlRYzIKkxGneIjGJdMXIGeuoQ== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=pass header.d=online.de header.s=s42582890 header.b=Q0kLDM8E; dmarc=pass (policy=none) header.from=online.de; spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org" ARC-Seal: i=1; s=key1; d=yhetil.org; t=1713090081; a=rsa-sha256; cv=none; b=Gp+IRbv6tFB1ktkxDf2sL1DJpCyyten+Js139UH4mJMgy/i88RDkJTxJ1imXfRAisK23HY 7kvnBg/tGru6VXSCPaEXmG7Fiace/ssDNc8LkCWAFYjJRyep3ZmnN0q4BFXqKTTzwjvS73 6T8iU4bNTZedl0YPVfmPkskBRQL03jMz1f1VGwkhSKEvPcpbXe8THRKnkGf1Uv3Qsf7lnx 6yfySeDWNFAM7Dxph5F/vMWcgpWhav1VyX3gI8UPs+GjDl9dcXyD1SKteAzkZ5dERmXEGm SmG1ptsLr5qLBoD1CxmU0nEJ3dDOtHCkO2xgO4qAgL68NW+5IHGK3nULNLJp4g== Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id C6A223B7D2 for ; Sun, 14 Apr 2024 12:21:21 +0200 (CEST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1rvwyE-0002WN-0a; Sun, 14 Apr 2024 06:20:22 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rvwy9-0002Vg-Fi for emacs-orgmode@gnu.org; Sun, 14 Apr 2024 06:20:17 -0400 Received: from mout.kundenserver.de ([212.227.126.130]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rvwy6-00081r-IC for emacs-orgmode@gnu.org; Sun, 14 Apr 2024 06:20:17 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=online.de; s=s42582890; t=1713090005; x=1713694805; i=bch@online.de; bh=Ht7aMNxHpHU+4VDHPSoeLDWCDaU1Ht1LtGPVSvZnnd8=; h=X-UI-Sender-Class:From:To:Subject:Date:Message-ID:MIME-Version: Content-Type:cc:content-transfer-encoding:content-type:date:from: message-id:mime-version:reply-to:subject:to; b=Q0kLDM8EvsOA47vkRle54NUywgc1oX7wR2BD5aDykkfdJQ4nJjm6YVx4IyfnoTfj mYYXbshJJxIXi/ABme9Tna0cuRIf+/4z/DDdsJLalefdVXDKa/5DF35cH8GMfet6a OtsOmJ9xOJzfSNoz5HveqqwoaGPkmffgpaGkrF5iv7rjFSo+jVFIGfWDN81Gb6m39 lxp5LRyEAuTgnE7g2fOd5QVrco5os0hN/GXsAKsVjErAx9DSGBa/gBhkZe3+M7jS0 aSCdI8yEnp2k/2yBFHurodE9980OSOd1YsqKSNRPEFIsrX/SX+/rCJt6p6kZDN0LJ /QEDjlXRALWXrdk8Qg== X-UI-Sender-Class: 6003b46c-3fee-4677-9b8b-2b628d989298 Received: from shell.onfire.org ([88.198.95.166]) by mrelayeu.kundenserver.de (mreue010 [212.227.15.167]) with ESMTPSA (Nemesis) id 1N9MYu-1ss9h23bQi-015Lxv; Sun, 14 Apr 2024 12:20:04 +0200 From: Christian Barthel To: emacs-orgmode@gnu.org Subject: Post to mastodon within Org Date: Sun, 14 Apr 2024 12:20:02 +0200 Message-ID: <874jc4qm99.fsf@shell.onfire.org> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.2 (berkeley-unix) X-Gpg-Key: https://onfire.org/bch/pgp/pubkey.asc X-Archive: expiry=7 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Provags-ID: V03:K1:GNTh7zvFbMHFdFNLmLZQiAC91SFkhHZQyV588+MTjo3uxZOVfMa ypS8qzDujGnrie7hL8aebYHRm/OosN+KspXBeXHfdMUsi40LGN2N8Zjcj3MfXil4HB7m2dY Zis4Z7MD1KY2J3guqJY9L2/MgPxo1Y9kcAsil7BtNrZZONf9wdcMqiA/mfiRvGqyXbV9NRY 8xJPIHzYJhDXq7qHvK+Hw== UI-OutboundReport: notjunk:1;M01:P0:dWAdVoDW/Nc=;J8Kk94kqvAsdQyO4eLUsxw10/el Kn04eK1+D/lTN5GwvyLrld2gL6UTNkAbioIZuLkj9A1ziZcyeMnmeKUVqcwFBT2jLO7CZcO5k uIBu4+/I5OsuePCgTaNfRozuUY98yQgZW4NGvXrqMiHqqbbtXZYURqjJMVcwVSZqrov9YQXnr gVHJ6F4Nf8JcJnPMuoxCUYZ95CvUicVbEmipQHP1tHceA3NQ4Mo7ONzSOYuiU04XlZIWCPPOf qLKwllp+AICFGj7YvxhIet7+Poed2/x6fQth3aIOIdFbjbftkrOxY3VC1dlS1ibxVw0WoBO7m J8ub7/ukVnovwG6VmLVE30pz2LTNCMe7oHoBpJX+E4/3o/mGVnsMQpQH09zCJw83EVS9C6Y6E VhhNdefdojEqhLs1ylAbiYGJgrW8ZGcZCNJqNYTG/3u+LilWvHAW8f3cy73Yb9XsHpiJqk9R0 o+12NlO/jr6K9JuZVeTWdo9604f/xJ24pwCeZ9+EeVIO1IhTvupVx684N4ozTaPhHuYUmI8dT DfpsdpJAI7c2Ow9rWlPTvT7q0y5iyyXWaOsB2eVAGASzqUfPjPtDvuYng59/JUIDlxRnaVQeF xyZZre4OS0N3c283cR5HQzulqVGuN4CQckqcJWNJ/yts2UytoBo6l96AFHQCDrdpPoyrW6XkR t+WiighpmhdekRLzaL/iY+fCp/q4hh4aNXKhNnAPQul/r/KzrUdPCxFcZNfCXBYFmheToyE24 9mc2pBCTVT3gdjJdlPN3kCJalI/7tMtvl8ZKYccXjN34PCrwgh01Bk= Received-SPF: pass client-ip=212.227.126.130; envelope-from=bch@online.de; helo=mout.kundenserver.de X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-orgmode@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+larch=yhetil.org@gnu.org Sender: emacs-orgmode-bounces+larch=yhetil.org@gnu.org X-Migadu-Flow: FLOW_IN X-Migadu-Country: US X-Migadu-Spam-Score: -7.55 X-Migadu-Scanner: mx11.migadu.com X-Spam-Score: -7.55 X-Migadu-Queue-Id: C6A223B7D2 X-TUID: t/OuQNX9BiAv --=-=-= Content-Type: text/plain Content-Transfer-Encoding: quoted-printable Hi, some time ago, I showed a screenshot how I am using Org-Mode to manage my mastodon status updates (posts/toos) [1] and I got some direct messages about sharing my elisp functions and I publicly [2] stated that the code is not production ready, but I will rework this. Sorry - it took a bit too long but with that mail, I want to share my defun's with you. First and most important, I do think that using [3] mastodon.el is way better than using my elisp functions. However, when you're interested in a handful, very small functions to post to mastodon, and manage them with org-mode, you may have a look at the attached file. Or someone is interested in polishing things up and adding an org-contrib package for this? My process: - Use org-capture to create a new mastodon status update (at least, tag it with :toot:) - Write a message - add images with "attachment:/" URLs. Those images will be uploaded as media and integrated in the status update - call `x/mastodon-post' - after success, PROPERTIES will be added like Mastodon ID, URL/URI and Media IDs - optionally, create a single-file Post with the org-dispatch interface (this is a custom export backend that is not included with this email, check [4] for further details how to set things up) A reply to an existing mastodon post is sent when a Mastodon_ID is found in the properties drawer one level above. This is what that looks like with my setup: Currently, I do have one dependency (requests.el). I would have loved to remove this and use url-retireve-synchronously but I were not able to upload binary data with it. If anyone has an idea on that, please check out [5]. Please note: I bcc'ed to mastodon Users who replied to the thread/direct messaged me and I was able to derive an email address - so they get some form of follow up notification on this. [1] https://x.bch.at/@bch/111659014415841582 [2] https://x.bch.at/@bch/111659254718879041 [3] https://codeberg.org/martianh/mastodon.el [4] https://orgmode.org/manual/Adding-Export-Back_002dends.html [5] https://lists.gnu.org/archive/html/help-gnu-emacs/2024-03/msg00182.htm= l =2D- Christian Barthel --=-=-= Content-Type: application/emacs-lisp Content-Disposition: attachment; filename=toots.el Content-Transfer-Encoding: quoted-printable ;; Contact: Christian Barthel ;; ;; Setup: ;; Set variables =E2=80=98mastodon_host=E2=80=99 and =E2=80=98mastodon_acce= ss_token=E2=80=99 ;; and eventually =E2=80=98request-curl-options=E2=80=99 ;; Usage: Posting a new status ;; (1) create Mastodon post i.e. with =E2=80=98org-capture=E2=80=99 ;; (=E2=80=98c-c t c T=E2=80=99) ;; (2) write the mastodon status update ;; (3) Optional: add images as attachments to ;; the org-item ;; (4) execute =E2=80=98x/mastodon-post=E2=80=99 ;; (5) execute =E2=80=98org-export-dispatch=E2=80=99. Create single ;; file export and upload it to =E2=80=98base-toots=E2=80=99 domain ;; Usage: Replying to a Mastodon status ;; (1) Ensure that the Org Node one level above has ;; the mastodon_id set as Org Property. ;; (2) Create a new toot one level below with =E2=80=98org-capture=E2=80=99 ;; (3) Execute =E2=80=98x/mastodon-post=E2=80=99 ;; Dependency: request.el ;;=20 ;; Looks like url-retrieve-synchronously is not able ;; to handle binary image uploads, therefore I use ;; requests/curl backend at the moment. ;; ;; Set connection options ;;; (setq request-curl-options nil) ;;; (setq request-curl-options (list "-x" "http://127.0.0.1:3128")) ;; Debugging for request.el: (setq request-log-level 'debug) ;; Set mastodon connection details: (setq mastodon_host "https://your-mastodon.host" mastodon_access_token "XXX") (defun x/post (body-string media replyid) "Send Mastodon Status Update This function sends a Mastodon status update to =E2=80=98mastodon_host=E2= =80=99 with =E2=80=98mastodon_access_token=E2=80=99. The =E2=80=98body-string=E2= =80=99 should be a non-nil string. =E2=80=98media=E2=80=99 is a list of IDs to link images = to the status and may be =E2=80=98nil=E2=80=99. The =E2=80=98replyid=E2=80=99= is an ID of an existing mastodon post. This can be nil. " (progn ;; Send Request: (request (format "%s/api/v1/statuses?access_token=3D%s" mastodon_host mastodon_access_token) :type "POST" :headers '(("Content-Type" . "application/json")) :data (json-encode (list (cons "status" body-string) (cons "in_reply_to_id" replyid) (cons "poll" nil) (cons "language" "en") (cons "sensitive" nil) (cons "visibility" "public") (if media (cons "media_ids" (split-string media ",")= ) (cons "media_ids" nil)) )) :parser 'json-read :sync t :complete (cl-function (lambda (&key data &allow-other-keys) (progn (message "I sent: %S" (assoc-default 'id data)) (org-entry-put (point-marker) "mastodon_id" (format "%s" (assoc-default 'id data)= )) (org-entry-put (point-marker) "mastodon_url" (format "%s" (assoc-default 'url data= ))) (org-entry-put (point-marker) "mastodon_uri" (format "%s" (assoc-default 'uri data= ))) (org-entry-put (point-marker) "mastodon_created_at" (format "%s" (assoc-default 'created_= at data))) data)))))) ;; Save the current-media ID list in the x/current-media ;; string (used between uploading media and setting this ;; value as an org-property) (setq x/current-media nil) (defun x/post-media (filename) "Post =E2=80=98filename=E2=80=99 as media to mastodon This function sends =E2=80=98filename=E2=80=99 to the mastodon API. The re= turned ID will be added to the list in =E2=80=98x/current-media=E2=80=99 as side e= ffect. See =E2=80=98x/post=E2=80=99 function as well. " (with-temp-buffer (insert-file-contents filename) (request (format "%s/api/v1/media" mastodon_host) :type "POST" :files `(("file" . ,(current-buffer))) :parser 'json-read :encoding 'binary :sync t :headers `(("Authorization" . ,(format "Bearer %s" mastodon_access_to= ken))) :success (cl-function (lambda (&key data &allow-other-keys) (progn (setq x/current-media (concat x/current-media (if x/current-media "," "") (format "%s" (assoc-default 'id data)= ))))))))) (defun x/get-reply-id () "Get reply ID for post, return =E2=80=98nil=E2=80=99 if not existent This function looks up MASTODON_ID in the parent org-item. When an ID is available, the Mastodon status update will be sent as reply. " (interactive) (plist-get (plist-get (plist-get (plist-get (org-element-context) 'headline) :parent) 'headline) :MASTODON_ID)) (defun x/upload-media () "Search for images, upload them to mastodon Assumptions: images are inserted with org-attach, a DIR property is created and a full path is being constructed by using DIR and the relative filename. " (let ((list-of-ids '()) (folder (org-entry-get (point) "DIR"))) (save-excursion (progn (setq x/current-media nil) (org-narrow-to-subtree) (goto-char (point-min)) ;; find all attachments ;; upload them (while (re-search-forward "attachment:*" nil t) (progn (x/post-media (format "%s/%s" folder (substring-no-properties (thing-at-point 'filename) (length "attachment:")))))) (org-entry-put (point-marker) "mastodon_media" (or x/current-media = "nil")) (widen) list-of-ids)))) (defun x/upload-status () "Upload Mastodon Status Create a new Mastodon status entry or, when mastodon_id is being found in the org-item one level above (parent), reply to an existing mastodon status. Also link with media when =E2=80=98x/current-media=E2=80=99 is set. " (let ((folder (org-entry-get (point) "DIR")) (bodytext nil)) (save-excursion (progn (x/post (progn (org-narrow-to-subtree) (goto-char (point-min)) (setq bodytext (substring-no-properties (org-agenda-get-some-entry-text (point-marker) 250))) (widen) (replace-regexp-in-string "#\\+ATTR_ORG:.*" "" (replace-regexp-in-string "\[\[attachment:[/a-zA-Z0-9.]*\]\]" "= " bodytext))) x/current-media (x/get-reply-id) ))))) (defun x/mastodon-post () (interactive) (setq x/current-media nil) (x/upload-media) (x/upload-status) (setq x/current-media nil)) --=-=-=--