commit
08c32b8647
64
README.md
64
README.md
|
@ -1 +1,63 @@
|
||||||
Nag yourself when you post toots that make you feel bad.
|
# mastodon-clippy
|
||||||
|
A customisable nodejs clippy bot for mastodon.
|
||||||
|
|
||||||
|
`mastodon-clippy` notices when you are tooting about a topic that is bad for your health, and gently suggests you stop.
|
||||||
|
|
||||||
|
See an example in action at [auspol_clippy](https://ausglam.space/@auspol_clippy).
|
||||||
|
|
||||||
|
## configuration
|
||||||
|
|
||||||
|
`mastodon-clippy` takes all configuration as ENV variables:
|
||||||
|
|
||||||
|
* `CLIPPY_TOPIC` is the topic your clippy bot makes suggestions for. e.g. "auspol".
|
||||||
|
* `CLIPPY_DOMAIN` is the base domain for the Mastodon server your bot runs on _without a protocol_. e.g. "botsin.space"
|
||||||
|
* `CLIPPY_USER` is the username of the bot, e.g. "auspol_clippy".
|
||||||
|
* `CLIPPY_ACCESS_TOKEN` is the API access token for your bot.
|
||||||
|
|
||||||
|
## auto-config
|
||||||
|
|
||||||
|
Some settings for your bot account will be automatically set/overridden whenever the bot starts. These are:
|
||||||
|
|
||||||
|
```js
|
||||||
|
locked: false,
|
||||||
|
bot: true,
|
||||||
|
discoverable: true,
|
||||||
|
source: { privacy: 'private' }
|
||||||
|
```
|
||||||
|
That is, your bot must always:
|
||||||
|
|
||||||
|
* accept new followers
|
||||||
|
* declare it is a bot
|
||||||
|
* be discoverable on the server suggestions page
|
||||||
|
* post messages in "followers only" mode
|
||||||
|
|
||||||
|
## manual config
|
||||||
|
|
||||||
|
It does not appear to be possible to use the API to set accounts to hide their social graph. users should be able to use your bot without other people necessarily knowing, but the bot needs to follow them in order to work. Therefore you should manually select `Hide your network` in `https://example.com/settings/preferences/other`.
|
||||||
|
|
||||||
|
## setup
|
||||||
|
|
||||||
|
You can use the example systemd file at `mastodon-clippy.service.example` tweaked to suit your setup. This will keep the bot running and set your environment variables as above.
|
||||||
|
|
||||||
|
Then run `npm install .` to install npm modules `axios` and `ws`.
|
||||||
|
|
||||||
|
# running
|
||||||
|
|
||||||
|
Start the bot with the traditional `node index.js`.
|
||||||
|
|
||||||
|
However, you probably want this to run automatically on a web server. You can do this with `systemd` using the example unit file: `mastodon-clippy.service.example`. Adjust this for your user and paths, and then activate it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo cp mastodon-clippy.service.example /etc/systemd/system/mastodon-clippy.service
|
||||||
|
sudo systemctl enable mastodon-clippy.service
|
||||||
|
sudo systemctl start mastodon-clippy service
|
||||||
|
```
|
||||||
|
|
||||||
|
## interacting with the bot
|
||||||
|
|
||||||
|
To "sign up" for notification from your bot, users have two options:
|
||||||
|
|
||||||
|
1. follow the bot account
|
||||||
|
2. send a toot to the bot with the word `START` in capital letters. e.g. `@auspol_clippy START`
|
||||||
|
|
||||||
|
To "unsubscribe" from the bot, users can send a toot with the word `STOP` in capital letters. e.g. `@auspol_clippy STOP`
|
||||||
|
|
42
index.js
42
index.js
|
@ -1,4 +1,4 @@
|
||||||
/*
|
/*
|
||||||
|
|
||||||
Clippy bot for mastodon.
|
Clippy bot for mastodon.
|
||||||
|
|
||||||
|
@ -53,25 +53,33 @@ function initiateSettings(socket) {
|
||||||
// return random suggestion string
|
// return random suggestion string
|
||||||
function suggestion(username) {
|
function suggestion(username) {
|
||||||
|
|
||||||
const n = crypto.randomInt(8)
|
const n = crypto.randomInt(12)
|
||||||
|
|
||||||
switch(n) {
|
switch(n) {
|
||||||
case 0:
|
case 0:
|
||||||
return 'How about logging off instead?';
|
return 'How about logging off instead?';
|
||||||
case 1:
|
case 1:
|
||||||
return `Would you like to delete your toot, ${username}?`;
|
return `Would you like to delete your toot, @${username}?`;
|
||||||
case 2:
|
case 2:
|
||||||
return 'Can I help you take a walk outside?';
|
return 'Can I help you take a walk outside? 🚶➡️';
|
||||||
case 3:
|
case 3:
|
||||||
return 'You may like to reconsider your life choices.';
|
return 'You may like to reconsider your life choices.';
|
||||||
case 4:
|
case 4:
|
||||||
return 'Why not try looking at #CatsOfMastodon instead?';
|
return 'Why not try looking at #CatsOfMastodon instead?';
|
||||||
case 5:
|
case 5:
|
||||||
return `Come on ${username}, we've talked about this.`;
|
return `Come on @${username}, we've talked about this. 🤷♂️`;
|
||||||
case 6:
|
case 6:
|
||||||
return `You should go look at some trees. Trees are calming`;
|
return `You should go look at some trees. Trees are calming 🌳`;
|
||||||
case 7:
|
case 7:
|
||||||
return `I'm not angry. I'm just very disappointed.`;
|
return `I'm not angry. I'm just very disappointed. 😔`;
|
||||||
|
case 8:
|
||||||
|
return `You said you were going to stop doing that ...and yet here we are.`;
|
||||||
|
case 9:
|
||||||
|
return `Time to touch some grass 🌱`;
|
||||||
|
case 10:
|
||||||
|
return `Why not have a nice cup of tea instead? 🫖`;
|
||||||
|
case 11:
|
||||||
|
return `And yet you still haven't read all of those books in your TBR pile. 🤔`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,42 +141,42 @@ function listen() {
|
||||||
|
|
||||||
// make sure bot is set up correctly each time it starts
|
// make sure bot is set up correctly each time it starts
|
||||||
initiateSettings(ws)
|
initiateSettings(ws)
|
||||||
|
|
||||||
// errors
|
// errors
|
||||||
ws.on('error', err => {
|
ws.on('error', err => {
|
||||||
console.error(`WebSocket error: ${err.message}`)
|
console.error(`WebSocket error: ${err.message}`)
|
||||||
resetConnection(ws)
|
resetConnection(ws)
|
||||||
})
|
})
|
||||||
|
|
||||||
// check updates and notifications in the stream
|
// check updates and notifications in the stream
|
||||||
ws.on('message', msg => {
|
ws.on('message', msg => {
|
||||||
let packet = JSON.parse(msg)
|
let packet = JSON.parse(msg)
|
||||||
let data = JSON.parse(packet.payload)
|
let data = JSON.parse(packet.payload)
|
||||||
|
|
||||||
// notifications
|
// notifications
|
||||||
if (packet.event == 'notification') {
|
if (packet.event == 'notification') {
|
||||||
|
|
||||||
// always follow back
|
// always follow back
|
||||||
if (data.type == 'follow') {
|
if (data.type == 'follow') {
|
||||||
followAction(data.account.id, 'follow')
|
followAction(data.account.id, 'follow')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.type == 'mention') {
|
if (data.type == 'mention') {
|
||||||
|
|
||||||
let post = data.status.content
|
let post = data.status.content
|
||||||
|
|
||||||
// check start requests
|
// check start requests
|
||||||
if (post.match(/\bSTART\b/)) {
|
if (post.match(/\bSTART\b/)) {
|
||||||
followAction(data.account.id, 'follow')
|
followAction(data.account.id, 'follow')
|
||||||
}
|
}
|
||||||
|
|
||||||
// check stop requests
|
// check stop requests
|
||||||
if (post.match(/\STOP\b/)) {
|
if (post.match(/\STOP\b/)) {
|
||||||
followAction(data.account.id, 'unfollow')
|
followAction(data.account.id, 'unfollow')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// updates (posts)
|
// updates (posts)
|
||||||
if (packet.event == 'update') {
|
if (packet.event == 'update') {
|
||||||
let rid = data.id
|
let rid = data.id
|
||||||
|
@ -185,7 +193,7 @@ function listen() {
|
||||||
}
|
}
|
||||||
else if (data.spoiler_text.toLowerCase().includes(topic)) {
|
else if (data.spoiler_text.toLowerCase().includes(topic)) {
|
||||||
sendResponse(rid, user, username)
|
sendResponse(rid, user, username)
|
||||||
}
|
}
|
||||||
else if (data.tags.map(tag => tag.name.toLowerCase()).includes(topic)) {
|
else if (data.tags.map(tag => tag.name.toLowerCase()).includes(topic)) {
|
||||||
sendResponse(rid, user, username)
|
sendResponse(rid, user, username)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"name": "mastodon-clippy",
|
"name": "mastodon-clippy",
|
||||||
"version": "1.1.1",
|
"version": "1.1.2",
|
||||||
"description": "Mastodon clippy bot",
|
"description": "Mastodon clippy bot",
|
||||||
"repository": "https://github.com/hughrun/mastodon-clippy.git",
|
"repository": "https://git.suboptimal.solutions/hugh/mastodon-clippy",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
"author": "Hugh Rundle <hugh@hughrundle.net> (https://www.hughrundle.net)",
|
"author": "Hugh Rundle <hugh@hughrundle.net> (https://www.hughrundle.net)",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.21.2",
|
"axios": "^1.7.3",
|
||||||
"ws": "^7.5.10"
|
"ws": "^8.18"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue