initial commit
This commit is contained in:
commit
75b679a533
4 changed files with 122 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
config.edn
|
||||||
|
package-lock.json
|
||||||
|
/node_modules
|
33
README.md
Normal file
33
README.md
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
### description
|
||||||
|
|
||||||
|
the bot will read the timeline from the specified Twitter accounts,
|
||||||
|
and post it to Mastodon
|
||||||
|
|
||||||
|
### installation
|
||||||
|
|
||||||
|
1. install [Node.js](https://nodejs.org/en/)
|
||||||
|
2. install [Lumo](https://github.com/anmonteiro/lumo): `npm install -g lumo-cljs`
|
||||||
|
3. run `npm install` to install Node modules
|
||||||
|
|
||||||
|
### usage
|
||||||
|
|
||||||
|
* create a Mastodon API key following the instructions [here](https://tinysubversions.com/notes/mastodon-bot/)
|
||||||
|
* create a Twitter API key follwing the instructions [here](https://developer.twitter.com/en/docs/basics/authentication/guides/access-tokens)
|
||||||
|
* create a file called `config.edn` with the following contents:
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
{:twitter {:access-keys
|
||||||
|
{:consumer_key "XXXX"
|
||||||
|
:consumer_secret "XXXX"
|
||||||
|
:access_token_key "XXXX"
|
||||||
|
:access_token_secret "XXXX"}
|
||||||
|
:accounts ["arstechnica" "WIRED"]} ;; accounts you wish to mirror
|
||||||
|
:mastodon {:access_token "XXXX"
|
||||||
|
:api_url "https://botsin.space/api/v1/"}}
|
||||||
|
```
|
||||||
|
* the bot looks for `config.edn` at its relative path by default, an alternative location can be specified either using the `MASTODON_BOT_CONFIG` environment variable or passing the path to config as an argument
|
||||||
|
|
||||||
|
* run the bot: `./mastodon-bot.cljs`
|
||||||
|
* to poll at intervals setup a cron job such as:
|
||||||
|
|
||||||
|
*/30 * * * * mastodon-bot.cljs /path/to/config.edn > /dev/null 2>&1
|
76
mastodon-bot.cljs
Executable file
76
mastodon-bot.cljs
Executable file
|
@ -0,0 +1,76 @@
|
||||||
|
#!/usr/bin/env lumo
|
||||||
|
(ns mastodon-bot.core
|
||||||
|
(:require
|
||||||
|
[cljs.core :refer [*command-line-args*]]
|
||||||
|
[cljs.reader :as edn]
|
||||||
|
["fs" :as fs]
|
||||||
|
["https" :as https]
|
||||||
|
["mastodon-api" :as mastodon]
|
||||||
|
["twitter" :as twitter]))
|
||||||
|
|
||||||
|
(def config (-> (or (first *command-line-args*)
|
||||||
|
(-> js/process .-env .-MASTODON_BOT_CONFIG)
|
||||||
|
"config.edn")
|
||||||
|
fs/readFileSync
|
||||||
|
str
|
||||||
|
edn/read-string))
|
||||||
|
(def mastodon-client (mastodon. (-> config :mastodon clj->js)))
|
||||||
|
(def twitter-client (twitter. (-> config :twitter :access-keys clj->js)))
|
||||||
|
(def twitter-accounts (-> config :twitter :accounts))
|
||||||
|
|
||||||
|
(defn js->edn [data]
|
||||||
|
(js->clj data :keywordize-keys true))
|
||||||
|
|
||||||
|
(defn delete-status [status]
|
||||||
|
(.delete mastodon-client (str "statuses/" status) #js {}))
|
||||||
|
|
||||||
|
(defn post-status
|
||||||
|
([status-text]
|
||||||
|
(post-status status-text nil))
|
||||||
|
([status-text media-ids]
|
||||||
|
(.post mastodon-client "statuses"
|
||||||
|
(clj->js (merge {:status status-text}
|
||||||
|
(when media-ids {:media_ids media-ids}))))))
|
||||||
|
|
||||||
|
(defn post-image [image-stream description callback]
|
||||||
|
(-> (.post mastodon-client "media" #js {:file image-stream :description description})
|
||||||
|
(.then #(-> % .-data .-id callback))))
|
||||||
|
|
||||||
|
(defn post-status-with-images
|
||||||
|
([status-text urls]
|
||||||
|
(post-status-with-images status-text urls []))
|
||||||
|
([status-text [url & urls] ids]
|
||||||
|
(if url
|
||||||
|
(.get https url
|
||||||
|
(fn [image-stream]
|
||||||
|
(post-image image-stream status-text #(post-status-with-images status-text urls (conj ids %)))))
|
||||||
|
(post-status status-text (not-empty ids)))))
|
||||||
|
|
||||||
|
(defn get-mastodon-timeline [callback]
|
||||||
|
(.then (.get mastodon-client "timelines/home" #js {}) #(-> % .-data js->edn callback)))
|
||||||
|
|
||||||
|
(defn parse-tweet [{created-at :created_at
|
||||||
|
text :text
|
||||||
|
{:keys [media]} :extended_entities
|
||||||
|
{:keys [screen_name]} :user :as tweet}]
|
||||||
|
{:created-at (js/Date. created-at)
|
||||||
|
:text (str text "\n - " screen_name)
|
||||||
|
:media-links (keep #(when (= (:type %) "photo") (:media_url_https %)) media)})
|
||||||
|
|
||||||
|
(defn post-tweets [last-post-time]
|
||||||
|
(fn [error tweets response]
|
||||||
|
(when tweets
|
||||||
|
(doseq [{:keys [text media-links]} (->> (js->edn tweets)
|
||||||
|
(map parse-tweet)
|
||||||
|
(filter #(> (:created-at %) last-post-time)))]
|
||||||
|
(if media-links
|
||||||
|
(post-status-with-images text media-links)
|
||||||
|
(post-status text))))))
|
||||||
|
|
||||||
|
(get-mastodon-timeline
|
||||||
|
(fn [timeline]
|
||||||
|
(doseq [account twitter-accounts]
|
||||||
|
(.get twitter-client
|
||||||
|
"statuses/user_timeline"
|
||||||
|
#js {:screen_name account :include_rts false}
|
||||||
|
(-> timeline first :created_at (js/Date.) post-tweets)))))
|
10
package.json
Normal file
10
package.json
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"name": "mastodon-bot",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"repository": "https://github.com/yogthos/mastodon-bot",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"mastodon-api": "1.3.0",
|
||||||
|
"twitter": "1.7.1"
|
||||||
|
}
|
||||||
|
}
|
Reference in a new issue