{"id":14384601,"url":"https://github.com/hobbyquaker/waldorf","last_synced_at":"2025-08-30T06:42:28.561Z","repository":{"id":57397043,"uuid":"85853723","full_name":"hobbyquaker/waldorf","owner":"hobbyquaker","description":"Simple Mattermost Bot 🤡🤠","archived":false,"fork":false,"pushed_at":"2018-03-12T19:20:10.000Z","size":12,"stargazers_count":14,"open_issues_count":1,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-28T06:51:13.315Z","etag":null,"topics":["bot","chat","javascript","mattermost","nodejs"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hobbyquaker.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-03-22T16:54:28.000Z","updated_at":"2022-12-11T00:37:46.000Z","dependencies_parsed_at":"2022-09-26T17:00:54.384Z","dependency_job_id":null,"html_url":"https://github.com/hobbyquaker/waldorf","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hobbyquaker%2Fwaldorf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hobbyquaker%2Fwaldorf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hobbyquaker%2Fwaldorf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hobbyquaker%2Fwaldorf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hobbyquaker","download_url":"https://codeload.github.com/hobbyquaker/waldorf/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248886334,"owners_count":21177645,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bot","chat","javascript","mattermost","nodejs"],"created_at":"2024-08-28T18:01:30.219Z","updated_at":"2025-04-14T18:22:53.336Z","avatar_url":"https://github.com/hobbyquaker.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# Waldorf\n\n[mit-badge]: https://img.shields.io/badge/License-MIT-blue.svg?style=flat\n[mit-url]: LICENSE\n[![License][mit-badge]][mit-url]\n[![NPM version](https://badge.fury.io/js/waldorf.svg)](http://badge.fury.io/js/waldorf)\n[![Dependency Status](https://david-dm.org/hobbyquaker/waldorf/status.svg)](https://david-dm.org/hobbyquaker/waldorf)\n[![devDependency Status](https://david-dm.org/hobbyquaker/waldorf/dev-status.svg)](https://david-dm.org/hobbyquaker/waldorf?type=dev)\n\n\u003e Simple Mattermost Bot 🤡🤠\n\nUses Webhooks\n\n\n# Installation\n\n* Prerequisites: [Node.js](https://nodejs.org)\n* Install Waldorf `sudo npm install -g waldorf`\n* Create a directory for the Scripts e.g. `mkdir /opt/waldorf`\n\n\n# Mattermost Setup\n\n* In the System Console - Integrations - Custom Integrations:\n    * Enable Incoming Webhooks\n    * Enable Outgoing Webhooks\n    * Enable Integrations to Override Usernames\n     \n* Create the Webhooks in the Team Settings - Integrations\n    * an Incoming Webhook for every Channel where Waldorf should be able to say something\n    * an Outgoing Webhook, you don't need to select a Channel here - then Waldorf will be able to subscribe to messages \n    in every Channel. Define desired Trigger Words, e.g. \"@waldorf\". As Callback URL you need to supply the IP Address\n    and the Port where Waldorf listens, if Waldorf runs on the same server as Mattermost you can use e.g. \n    http://127.0.0.1:31337\n    \n    \n# Start Waldorf\n\nSee `waldorf --help` for available options.\n\nExample Start command:\n```\nwaldorf -u http://127.0.0.1:8065/hooks/ \\\n    -n waldorf \\\n    -s /opt/waldorf \\\n    -t s1zz8e1wxzgwjfmsnz3c43dnpa \\\n    -c ij6osdf3ofnidp199ronuinwne:town-square \\\n    -c hiirtud1spfwmfegd3pejamzsr:another-channel \n```\nThe -t option supplies the Secret Mattermost generated for the Outgoing Webhook, the -c options define Channels and the \nSecrets of the Incoming Webhooks.\n\nI suggest to use [PM2](http://pm2.keymetrics.io/) to start Waldorf.\n\n\n# Scripts\n\nJust place Javascript Files in the /opt/waldorf folder and mind that you have to restart Waldorf when you change or add\nScripts there.\n\nExample Scripts:\n```javascript\n// Stupid :)\nschedule('37 13 * * *', () =\u003e pub('town-square', '1337 time!!1! 🤓'));\n```\n\n```javascript\n// Respond \"Hi @user\" when someone says \"Hello\" or \"hallo\" ...\nsub(/[Hh][ea]llo/, (match, user, channel) =\u003e pub(channel, `Hi @${user}`));\n```\n\n```javascript\n// simple quote script\nconst fs = require('fs');\nconst file = '/opt/waldorf/quotes.json';\n\nlet quotes = [];\n\nif (fs.existsSync(file)) quotes = JSON.parse(fs.readFileSync(file));\n\nsub(/\\!addquote (.*)/, (match, user, channel) =\u003e {\n    quotes.push(match[1]);\n    fs.writeFileSync(file, JSON.stringify(quotes));\n});\n\nsub(/\\!randomquote/, (text, user, channel) =\u003e {\n    pub(channel, '\u003e ' + quotes[Math.floor(Math.random() * quotes.length)]);\n});\n```\n\n# Script API\n\n## Classes\n\n\u003cdl\u003e\n\u003cdt\u003e\u003ca href=\"#log\"\u003elog\u003c/a\u003e\u003c/dt\u003e\n\u003cdd\u003e\u003cp\u003eLog to stdout/stderr. Messages are prefixed with a timestamp and the calling scripts path.\u003c/p\u003e\n\u003c/dd\u003e\n\u003c/dl\u003e\n\n## Functions\n\n\u003cdl\u003e\n\u003cdt\u003e\u003ca href=\"#pub\"\u003epub(channel, text)\u003c/a\u003e\u003c/dt\u003e\n\u003cdd\u003e\u003cp\u003eSend Text to a Channel\u003c/p\u003e\n\u003c/dd\u003e\n\u003cdt\u003e\u003ca href=\"#sub\"\u003esub(pattern, callback)\u003c/a\u003e ⇒ \u003ccode\u003esubscriptionId\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003e\u003cp\u003eAdd a Subscription that calls a Callback when pattern matches text said in a Channel\u003c/p\u003e\n\u003c/dd\u003e\n\u003cdt\u003e\u003ca href=\"#unsub\"\u003eunsub(id)\u003c/a\u003e\u003c/dt\u003e\n\u003cdd\u003e\u003cp\u003eRemove a Subscription\u003c/p\u003e\n\u003c/dd\u003e\n\u003cdt\u003e\u003ca href=\"#schedule\"\u003eschedule(pattern, [options], callback)\u003c/a\u003e\u003c/dt\u003e\n\u003cdd\u003e\u003cp\u003eSchedule recurring and one-shot events\u003c/p\u003e\n\u003c/dd\u003e\n\u003c/dl\u003e\n\n## Typedefs\n\n\u003cdl\u003e\n\u003cdt\u003e\u003ca href=\"#subscribeCallback\"\u003esubscribeCallback\u003c/a\u003e : \u003ccode\u003efunction\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003e\u003c/dd\u003e\n\u003c/dl\u003e\n\n\u003ca name=\"log\"\u003e\u003c/a\u003e\n\n## log\nLog to stdout/stderr. Messages are prefixed with a timestamp and the calling scripts path.\n\n**Kind**: global class  \n\n* [log](#log)\n    * [.debug()](#log.debug)\n    * [.info()](#log.info)\n    * [.warn()](#log.warn)\n    * [.error()](#log.error)\n\n\u003ca name=\"log.debug\"\u003e\u003c/a\u003e\n\n### log.debug()\nLog a debug message\n\n**Kind**: static method of \u003ccode\u003e[log](#log)\u003c/code\u003e  \n\n| Type |\n| --- |\n| \u003ccode\u003e\\*\u003c/code\u003e | \n\n\u003ca name=\"log.info\"\u003e\u003c/a\u003e\n\n### log.info()\nLog an info message\n\n**Kind**: static method of \u003ccode\u003e[log](#log)\u003c/code\u003e  \n\n| Type |\n| --- |\n| \u003ccode\u003e\\*\u003c/code\u003e | \n\n\u003ca name=\"log.warn\"\u003e\u003c/a\u003e\n\n### log.warn()\nLog a warning message\n\n**Kind**: static method of \u003ccode\u003e[log](#log)\u003c/code\u003e  \n\n| Type |\n| --- |\n| \u003ccode\u003e\\*\u003c/code\u003e | \n\n\u003ca name=\"log.error\"\u003e\u003c/a\u003e\n\n### log.error()\nLog an error message\n\n**Kind**: static method of \u003ccode\u003e[log](#log)\u003c/code\u003e  \n\n| Type |\n| --- |\n| \u003ccode\u003e\\*\u003c/code\u003e | \n\n\u003ca name=\"pub\"\u003e\u003c/a\u003e\n\n## pub(channel, text)\nSend Text to a Channel\n\n**Kind**: global function  \n\n| Param | Type |\n| --- | --- |\n| channel | \u003ccode\u003estring\u003c/code\u003e | \n| text | \u003ccode\u003estring\u003c/code\u003e | \n\n\u003ca name=\"sub\"\u003e\u003c/a\u003e\n\n## sub(pattern, callback) ⇒ \u003ccode\u003esubscriptionId\u003c/code\u003e\nAdd a Subscription that calls a Callback when pattern matches text said in a Channel\n\n**Kind**: global function  \n\n| Param | Type |\n| --- | --- |\n| pattern | \u003ccode\u003estring\u003c/code\u003e \\| \u003ccode\u003eRegExp\u003c/code\u003e | \n| callback | \u003ccode\u003e[subscribeCallback](#subscribeCallback)\u003c/code\u003e | \n\n**Example**  \n```js\n// Respond \"Hi @User\" when someone says \"Hello\" or \"hello\"\nsub(/[Hh]ello/, (match, user, channel) =\u003e pub(`Hi @${user}`));\n```\n\u003ca name=\"unsub\"\u003e\u003c/a\u003e\n\n## unsub(id)\nRemove a Subscription\n\n**Kind**: global function  \n\n| Param | Type |\n| --- | --- |\n| id | \u003ccode\u003esubscriptionId\u003c/code\u003e | \n\n\u003ca name=\"schedule\"\u003e\u003c/a\u003e\n\n## schedule(pattern, [options], callback)\nSchedule recurring and one-shot events\n\n**Kind**: global function  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| pattern | \u003ccode\u003estring\u003c/code\u003e \\| \u003ccode\u003eDate\u003c/code\u003e \\| \u003ccode\u003eObject\u003c/code\u003e \\| \u003ccode\u003eArray.\u0026lt;mixed\u0026gt;\u003c/code\u003e | pattern or array of patterns. May be cron style string, Date object or node-schedule object literal. See [https://github.com/tejasmanohar/node-schedule/wiki](https://github.com/tejasmanohar/node-schedule/wiki) |\n| [options] | \u003ccode\u003eObject\u003c/code\u003e |  |\n| [options.random] | \u003ccode\u003enumber\u003c/code\u003e | random delay execution in seconds. Has to be positive |\n| callback | \u003ccode\u003efunction\u003c/code\u003e | is called with no arguments |\n\n**Example**  \n```js\n// every full Hour.\nschedule('0 * * * *', callback);\n\n// Monday till friday, random between 7:30am an 8:00am\nschedule('30 7 * * 1-5', {random: 30 * 60}, callback);\n\n// once on 21. December 2018 at 5:30am\nschedule(new Date(2018, 12, 21, 5, 30, 0), callback);\n\n// every Sunday at 2:30pm\nschedule({hour: 14, minute: 30, dayOfWeek: 0}, callback);\n```\n\u003ca name=\"subscribeCallback\"\u003e\u003c/a\u003e\n\n## subscribeCallback : \u003ccode\u003efunction\u003c/code\u003e\n**Kind**: global typedef  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| text | \u003ccode\u003estring\u003c/code\u003e \\| \u003ccode\u003eArray.\u0026lt;string\u0026gt;\u003c/code\u003e | text or .match(RegExp) array |\n| user | \u003ccode\u003estring\u003c/code\u003e | the Name of the User that said something |\n| channel | \u003ccode\u003estring\u003c/code\u003e | the Channel where something was said |\n\n\n# License\n\nMIT (c) Copyright Sebastian Raff\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhobbyquaker%2Fwaldorf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhobbyquaker%2Fwaldorf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhobbyquaker%2Fwaldorf/lists"}