Compare commits
1 Commits
yarn
...
improved-b
Author | SHA1 | Date | |
---|---|---|---|
b0ac3ea19c |
@@ -2,7 +2,7 @@
|
|||||||
<p>Doorbell bot for matrix</p>
|
<p>Doorbell bot for matrix</p>
|
||||||
<p>The bot will only respond & join in authorized rooms (add authorized room IDs in `config.json` => `rooms`).
|
<p>The bot will only respond & join in authorized rooms (add authorized room IDs in `config.json` => `rooms`).
|
||||||
NOTE: The bot needs to have the room authorized before being invited. If the bot is invited, then has the room authorized in the config, it <b>will not</b> join!</p>
|
NOTE: The bot needs to have the room authorized before being invited. If the bot is invited, then has the room authorized in the config, it <b>will not</b> join!</p>
|
||||||
<p>The commands to trigger the doorbell is: "doorbell", "open", "ring", "knock", "ding", "dong" & "dingdong"</p>
|
<p>The commands to trigger the doorbell are defined in the config file</p>
|
||||||
|
|
||||||
# How to run:
|
# How to run:
|
||||||
## Step 1: Getting an access token
|
## Step 1: Getting an access token
|
||||||
@@ -20,6 +20,7 @@ NOTE: The bot needs to have the room authorized before being invited. If the bot
|
|||||||
- Fill in: `homeserver` (`config.json`) (This should already be filled if step `1.1` was done)
|
- Fill in: `homeserver` (`config.json`) (This should already be filled if step `1.1` was done)
|
||||||
- Fill in: `token` (`config.json`) (This is the token you got in step `1.2`)
|
- Fill in: `token` (`config.json`) (This is the token you got in step `1.2`)
|
||||||
- Fill in: `prefix` (`config.json`)
|
- Fill in: `prefix` (`config.json`)
|
||||||
|
- Fill in: `commands` (`config.json`) (Commands are case insensitive, and should not include the prefix)
|
||||||
- Fill in: `rooms` (`config.json`)
|
- Fill in: `rooms` (`config.json`)
|
||||||
- Fill in: `doorbellWebhook` (`config.json`)
|
- Fill in: `doorbellWebhook` (`config.json`)
|
||||||
### Step 2.2: Actually running the bot
|
### Step 2.2: Actually running the bot
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
"homeserver": "HOME_SERVER",
|
"homeserver": "HOME_SERVER",
|
||||||
"token": "ACCESS_TOKEN",
|
"token": "ACCESS_TOKEN",
|
||||||
"prefix": "BOT_PREFIX",
|
"prefix": "BOT_PREFIX",
|
||||||
|
"commands": ["COMMAND1", "COMMAND2"],
|
||||||
"rooms": ["AUTHORIZED_ROOM_ID", "ANOTHER_AUTHORIZED_ID"],
|
"rooms": ["AUTHORIZED_ROOM_ID", "ANOTHER_AUTHORIZED_ID"],
|
||||||
"doorbellWebhook": "https://GOOGLE.ASSISTANT.WEBHOOK.URL/"
|
"doorbellWebhook": "https://GOOGLE.ASSISTANT.WEBHOOK.URL/"
|
||||||
}
|
}
|
34
flake.lock
generated
34
flake.lock
generated
@@ -1,23 +1,5 @@
|
|||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"flake-utils": {
|
|
||||||
"inputs": {
|
|
||||||
"systems": "systems"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1710146030,
|
|
||||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1723637854,
|
"lastModified": 1723637854,
|
||||||
@@ -36,24 +18,8 @@
|
|||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": "flake-utils",
|
|
||||||
"nixpkgs": "nixpkgs"
|
"nixpkgs": "nixpkgs"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"systems": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681028828,
|
|
||||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": "root",
|
"root": "root",
|
||||||
|
67
flake.nix
67
flake.nix
@@ -1,38 +1,41 @@
|
|||||||
{
|
{
|
||||||
description = "PVV doorbell matrix bot";
|
description = "PVV doorbell matrix bot";
|
||||||
|
|
||||||
inputs = {
|
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
outputs = { self, nixpkgs, ... }@inputs: let
|
||||||
|
forAllSystems = nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed;
|
||||||
|
in {
|
||||||
|
inherit inputs;
|
||||||
|
|
||||||
|
packages = forAllSystems (system: let
|
||||||
|
inherit (pkgs) lib;
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
package-json = lib.importJSON ./package.json;
|
||||||
|
in {
|
||||||
|
|
||||||
|
default = pkgs.buildNpmPackage {
|
||||||
|
pname = package-json.name;
|
||||||
|
version = package-json.version;
|
||||||
|
meta.homepage = package-json.repository.url;
|
||||||
|
meta.license = lib.getLicenseFromSpdxId package-json.license;
|
||||||
|
|
||||||
|
src = lib.fileset.toSource {
|
||||||
|
root = ./.;
|
||||||
|
fileset = lib.fileset.difference ./. (lib.fileset.unions [
|
||||||
|
./flake.nix
|
||||||
|
./flake.lock
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
npmDepsHash = "sha256-UNc902yMkoWfpun1RrLYlEtOXcFd7uxlwKFWoM0/nTE=";
|
||||||
|
|
||||||
|
postInstall = ''
|
||||||
|
ln -vs /run/secrets/pvv-doorbell-config.json $out/lib/node_modules/$pname/config.json
|
||||||
|
'';
|
||||||
|
dontNpmBuild = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = { self, nixpkgs, flake-utils, ... }@inputs:
|
|
||||||
flake-utils.lib.eachDefaultSystem (system:
|
|
||||||
let
|
|
||||||
pkgs = nixpkgs.legacyPackages.${system};
|
|
||||||
node-modules = pkgs.mkYarnPackage {
|
|
||||||
name = "node-modules";
|
|
||||||
src = ./.;
|
|
||||||
};
|
|
||||||
frontend = pkgs.stdenv.mkDerivation {
|
|
||||||
name = "frontend";
|
|
||||||
src = ./.;
|
|
||||||
buildInputs = [pkgs.yarn node-modules];
|
|
||||||
buildPhase = ''
|
|
||||||
ln -s ${node-modules}/libexec/pvv-doorbell-bot/node_modules node_modules
|
|
||||||
${pkgs.yarn}/bin/yarn build
|
|
||||||
'';
|
|
||||||
installPhase = ''
|
|
||||||
mkdir $out
|
|
||||||
mv dist $out/lib
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
in {
|
|
||||||
packages = {
|
|
||||||
node-modules = node-modules;
|
|
||||||
default = frontend;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
24
index.mjs
24
index.mjs
@@ -7,13 +7,12 @@ import {
|
|||||||
|
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
import { env } from 'node:process';
|
import config from "./config.json" assert {type: "json"};
|
||||||
|
|
||||||
const config = await import (env.DOORBELL_CONFIG_FILE || "./config.json"); // assert {type: "json"};
|
|
||||||
|
|
||||||
const homeserverUrl = config.homeserver;
|
const homeserverUrl = config.homeserver;
|
||||||
const token = config.token;
|
const token = config.token;
|
||||||
const prefix = config.prefix;
|
const prefix = config.prefix;
|
||||||
|
const commandsRaw = config.commands;
|
||||||
const rooms = config.rooms;
|
const rooms = config.rooms;
|
||||||
const doorbellWebhook = config.doorbellWebhook;
|
const doorbellWebhook = config.doorbellWebhook;
|
||||||
|
|
||||||
@@ -24,9 +23,12 @@ function configNotFound(name) {
|
|||||||
if (!homeserverUrl) configNotFound("homeserver");
|
if (!homeserverUrl) configNotFound("homeserver");
|
||||||
if (!token) configNotFound("token");
|
if (!token) configNotFound("token");
|
||||||
if (!prefix) configNotFound("prefix");
|
if (!prefix) configNotFound("prefix");
|
||||||
|
if (!commandsRaw) configNotFound("commands");
|
||||||
if (!rooms) configNotFound("rooms");
|
if (!rooms) configNotFound("rooms");
|
||||||
if (!doorbellWebhook) configNotFound("doorbellWebhook");
|
if (!doorbellWebhook) configNotFound("doorbellWebhook");
|
||||||
|
|
||||||
|
const commands = commandsRaw.map(cmd => cmd.toLowerCase());
|
||||||
|
|
||||||
// We'll want to make sure the bot doesn't have to do an initial sync every
|
// We'll want to make sure the bot doesn't have to do an initial sync every
|
||||||
// time it restarts, so we need to prepare a storage provider. Here we use
|
// time it restarts, so we need to prepare a storage provider. Here we use
|
||||||
// a simple JSON database.
|
// a simple JSON database.
|
||||||
@@ -50,6 +52,12 @@ client.on("room.invite", (roomId) => {
|
|||||||
// client up. This will start it syncing.
|
// client up. This will start it syncing.
|
||||||
client.start().then(() => console.log("Client started!"));
|
client.start().then(() => console.log("Client started!"));
|
||||||
|
|
||||||
|
function noDingDong(client, roomId, event, error) {
|
||||||
|
console.log("No ding dong :(");
|
||||||
|
console.log(error);
|
||||||
|
client.replyNotice(roomId, event, "Couldn't dingdong the doorbell :(");
|
||||||
|
}
|
||||||
|
|
||||||
// This is our event handler for dealing with commands.
|
// This is our event handler for dealing with commands.
|
||||||
async function handleCommand(roomId, event) {
|
async function handleCommand(roomId, event) {
|
||||||
// Don't handle events that don't have contents (they were probably redacted)
|
// Don't handle events that don't have contents (they were probably redacted)
|
||||||
@@ -71,16 +79,16 @@ async function handleCommand(roomId, event) {
|
|||||||
if (!rawText || !rawText.startsWith(prefix)) return;
|
if (!rawText || !rawText.startsWith(prefix)) return;
|
||||||
const text = rawText.substring(prefix.length);
|
const text = rawText.substring(prefix.length);
|
||||||
|
|
||||||
if (["doorbell", "open", "ring", "knock", "ding", "dong", "dingdong"].includes(text)) {
|
if (commands.some(cmd => text.toLowerCase().startsWith(cmd))) {
|
||||||
|
|
||||||
fetch(doorbellWebhook, { method: 'POST' }).then(response => {
|
fetch(doorbellWebhook, { method: 'POST' }).then(response => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
console.log("DING DONG!");
|
console.log("DING DONG!");
|
||||||
|
client.replyNotice(roomId, event, "Doorbell dingdonged!");
|
||||||
} else {
|
} else {
|
||||||
console.log("No ding dong :(");
|
noDingDong(client, roomId, event, response);
|
||||||
console.log(response);
|
|
||||||
}
|
}
|
||||||
});
|
}).catch(err => noDingDong(client, roomId, event, err));
|
||||||
}
|
}
|
||||||
|
|
||||||
var tags = [];
|
var tags = [];
|
||||||
@@ -132,4 +140,4 @@ async function handleCommand(roomId, event) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
client.sendMessage(roomId, event);
|
client.sendMessage(roomId, event);
|
||||||
}
|
}
|
61
module.nix
61
module.nix
@@ -1,61 +0,0 @@
|
|||||||
{ config, lib, pkgs, ... }:
|
|
||||||
let
|
|
||||||
cfg = config.services.pvv-doorbell-bot;
|
|
||||||
inherit (lib) mkDefault mkEnableOption mkPackageOption mkIf mkOption types mdDoc;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.services.pvv-doorbell-bot = {
|
|
||||||
enable = mkEnableOption (lib.mdDoc "Enable the doorbell service that alerts on matrix/discord pings");
|
|
||||||
|
|
||||||
package = mkPackageOption pkgs "pvv-doorbell-bot" { };
|
|
||||||
nodePackage = mkPackageOption pkgs "nodejs_20" { };
|
|
||||||
|
|
||||||
user = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = "pvv-doorbell-bot";
|
|
||||||
};
|
|
||||||
|
|
||||||
group = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = "pvv-doorbell-bot";
|
|
||||||
};
|
|
||||||
|
|
||||||
settings = {
|
|
||||||
configFile = mkOption {
|
|
||||||
type = types.path;
|
|
||||||
description = mdDoc "Path to secret config.json file that sets the options defined in `config.json.template`";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
|
||||||
users.users = mkIf (cfg.user == "pvv-doorbell-bot") {
|
|
||||||
pvv-doorbell-bot = {
|
|
||||||
description = "PVV Doorbell Matrix Bot User";
|
|
||||||
isSystemUser = true;
|
|
||||||
group = cfg.group;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
users.groups = mkIf (cfg.group == "pvv-doorbell-bot") {
|
|
||||||
pvv-doorbell-bot = { };
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.services."pvv-doorbell-bot" = {
|
|
||||||
serviceConfig = let
|
|
||||||
appDir = "${cfg.package}/lib/node_modules/doorbell-matrix-bot";
|
|
||||||
in {
|
|
||||||
ExecStart = "${lib.getExe cfg.nodePackage} ${appDir}/index.mjs";
|
|
||||||
WorkingDirectory = appDir;
|
|
||||||
RuntimeDirectory = "pvv-doorbell-bot";
|
|
||||||
User = cfg.user;
|
|
||||||
Group = cfg.group;
|
|
||||||
};
|
|
||||||
|
|
||||||
environment = {
|
|
||||||
DOORBELL_CONFIG_FILE = cfg.settings.configFile;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
106
package-lock.json
generated
Normal file
106
package-lock.json
generated
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
{
|
||||||
|
"name": "doorbell-matrix-bot",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "doorbell-matrix-bot",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.7.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/asynckit": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||||
|
},
|
||||||
|
"node_modules/axios": {
|
||||||
|
"version": "1.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz",
|
||||||
|
"integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==",
|
||||||
|
"dependencies": {
|
||||||
|
"follow-redirects": "^1.15.6",
|
||||||
|
"form-data": "^4.0.0",
|
||||||
|
"proxy-from-env": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/combined-stream": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||||
|
"dependencies": {
|
||||||
|
"delayed-stream": "~1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/delayed-stream": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/follow-redirects": {
|
||||||
|
"version": "1.15.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
|
||||||
|
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"debug": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/form-data": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||||
|
"dependencies": {
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"combined-stream": "^1.0.8",
|
||||||
|
"mime-types": "^2.1.12"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mime-db": {
|
||||||
|
"version": "1.52.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mime-types": {
|
||||||
|
"version": "2.1.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||||
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-db": "1.52.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/proxy-from-env": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -13,7 +13,6 @@
|
|||||||
"author": "henrkgr",
|
"author": "henrkgr",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.7.4",
|
"axios": "^1.7.4"
|
||||||
"matrix-bot-sdk": "^0.7.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user