This commit is contained in:
		
						commit
						4e51dafdc0
					
				
					 12 changed files with 731 additions and 0 deletions
				
			
		
							
								
								
									
										6
									
								
								.auth.example
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.auth.example
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    "instructions": "Replace the following three values as appropriate, then rename this file to '.auth' (without '-template' at the end).",
 | 
				
			||||||
 | 
					    "discordToken": "discord token goes here",
 | 
				
			||||||
 | 
					    "trelloKey": "trello public key goes here",
 | 
				
			||||||
 | 
					    "trelloToken": "trello app token goes here"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										27
									
								
								.drone.yml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								.drone.yml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,27 @@
 | 
				
			||||||
 | 
					kind: pipeline
 | 
				
			||||||
 | 
					name: default
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					steps:
 | 
				
			||||||
 | 
					- name: install
 | 
				
			||||||
 | 
					  image: node:14.0
 | 
				
			||||||
 | 
					  commands:
 | 
				
			||||||
 | 
					  - npm install
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- name: deploy
 | 
				
			||||||
 | 
					  image: plugins/docker
 | 
				
			||||||
 | 
					  settings:
 | 
				
			||||||
 | 
					    registry: registry.cliffbreak.de
 | 
				
			||||||
 | 
					    repo: registry.cliffbreak.de/trellobot
 | 
				
			||||||
 | 
					    username:
 | 
				
			||||||
 | 
					      from_secret: docker_username
 | 
				
			||||||
 | 
					    password:
 | 
				
			||||||
 | 
					      from_secret: docker_password
 | 
				
			||||||
 | 
					  when:
 | 
				
			||||||
 | 
					    event:
 | 
				
			||||||
 | 
					    - push
 | 
				
			||||||
 | 
					    - tag
 | 
				
			||||||
 | 
					    - deployment
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					trigger:
 | 
				
			||||||
 | 
					  branch:
 | 
				
			||||||
 | 
					  - master
 | 
				
			||||||
							
								
								
									
										6
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					.auth
 | 
				
			||||||
 | 
					.latestActivityID
 | 
				
			||||||
 | 
					node_modules/
 | 
				
			||||||
 | 
					.vscode/
 | 
				
			||||||
 | 
					old-confs/
 | 
				
			||||||
 | 
					conf.json
 | 
				
			||||||
							
								
								
									
										9
									
								
								Dockerfile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								Dockerfile
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,9 @@
 | 
				
			||||||
 | 
					FROM node:14.0
 | 
				
			||||||
 | 
					ENV NODE_ENV dev
 | 
				
			||||||
 | 
					WORKDIR /usr/src/app
 | 
				
			||||||
 | 
					COPY package*.json ./
 | 
				
			||||||
 | 
					COPY .auth.example ./.auth
 | 
				
			||||||
 | 
					COPY conf.json.example ./conf.json
 | 
				
			||||||
 | 
					RUN npm install --production --silent
 | 
				
			||||||
 | 
					COPY . .
 | 
				
			||||||
 | 
					CMD npm start
 | 
				
			||||||
							
								
								
									
										16
									
								
								conf.json.example
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								conf.json.example
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,16 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    "boardIDs": [
 | 
				
			||||||
 | 
					        "tNbPCydx"
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "serverID": "138520312697454592",
 | 
				
			||||||
 | 
					    "channelID": "453042376898904080",
 | 
				
			||||||
 | 
					    "pollInterval": 10000,
 | 
				
			||||||
 | 
					    "contentString": "",
 | 
				
			||||||
 | 
					    "enabledEvents": [
 | 
				
			||||||
 | 
					        "cardCreated"
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "userIDs": {
 | 
				
			||||||
 | 
					        "theangush": "138520076427984896"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "prefix": "."
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										9
									
								
								docker-compose.yml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								docker-compose.yml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,9 @@
 | 
				
			||||||
 | 
					version: '2.1'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					services:
 | 
				
			||||||
 | 
					  trellobot:
 | 
				
			||||||
 | 
					    container_name: trellobot
 | 
				
			||||||
 | 
					    restart: always
 | 
				
			||||||
 | 
					    image: registry.cliffbreak.de/trellobot
 | 
				
			||||||
 | 
					    environment:
 | 
				
			||||||
 | 
					      NODE_ENV: production 
 | 
				
			||||||
							
								
								
									
										91
									
								
								events.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								events.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,91 @@
 | 
				
			||||||
 | 
					# Event Types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Supported Events:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Put any of these in the `enabledEvents` array of your `conf.json` file to utilize the event whitelist. They should all be self-explanatory.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `cardCreated`
 | 
				
			||||||
 | 
					* `cardDescriptionChanged`
 | 
				
			||||||
 | 
					* `cardDueDateChanged`
 | 
				
			||||||
 | 
					* `cardPositionChanged`
 | 
				
			||||||
 | 
					* `cardListChanged`
 | 
				
			||||||
 | 
					* `cardNameChanged`
 | 
				
			||||||
 | 
					* `cardUnarchived`
 | 
				
			||||||
 | 
					* `cardArchived`
 | 
				
			||||||
 | 
					* `cardDeleted`
 | 
				
			||||||
 | 
					* `commentEdited`
 | 
				
			||||||
 | 
					* `commentAdded`
 | 
				
			||||||
 | 
					* `memberAddedToCard`
 | 
				
			||||||
 | 
					* `memberAddedToCardBySelf`
 | 
				
			||||||
 | 
					* `memberRemovedFromCard`
 | 
				
			||||||
 | 
					* `memberRemovedFromCardBySelf`
 | 
				
			||||||
 | 
					* `listCreated`
 | 
				
			||||||
 | 
					* `listNameChanged`
 | 
				
			||||||
 | 
					* `listPositionChanged`
 | 
				
			||||||
 | 
					* `listUnarchived`
 | 
				
			||||||
 | 
					* `listArchived`
 | 
				
			||||||
 | 
					* `attachmentAddedToCard`
 | 
				
			||||||
 | 
					* `attachmentRemovedFromCard`
 | 
				
			||||||
 | 
					* `checklistAddedToCard`
 | 
				
			||||||
 | 
					* `checklistRemovedFromCard`
 | 
				
			||||||
 | 
					* `checklistItemMarkedComplete`
 | 
				
			||||||
 | 
					* `checklistItemMarkedIncomplete`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Unsupported Events:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					These are other events that *ostensibly exist*, but have not yet been implemented in Trellobot, or aren't available from the Trello API, so you can't get alerts for them.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `addAdminToBoard`
 | 
				
			||||||
 | 
					* `addAdminToOrganization`
 | 
				
			||||||
 | 
					* `addBoardsPinnedToMember`
 | 
				
			||||||
 | 
					* `addLabelToCard`
 | 
				
			||||||
 | 
					* `addMemberToBoard`
 | 
				
			||||||
 | 
					* `addMemberToOrganization`
 | 
				
			||||||
 | 
					* `addToOrganizationBoard`
 | 
				
			||||||
 | 
					* `convertToCardFromCheckItem`
 | 
				
			||||||
 | 
					* `copyBoard`
 | 
				
			||||||
 | 
					* `copyCard`
 | 
				
			||||||
 | 
					* `copyChecklist`
 | 
				
			||||||
 | 
					* `copyCommentCard`
 | 
				
			||||||
 | 
					* `createBoard`
 | 
				
			||||||
 | 
					* `createBoardInvitation`
 | 
				
			||||||
 | 
					* `createBoardPreference`
 | 
				
			||||||
 | 
					* `createChecklist` 
 | 
				
			||||||
 | 
					* `createLabel`
 | 
				
			||||||
 | 
					* `createOrganization`
 | 
				
			||||||
 | 
					* `createOrganizationInvitation`
 | 
				
			||||||
 | 
					* `deleteBoardInvitation`
 | 
				
			||||||
 | 
					* `deleteCheckItem`
 | 
				
			||||||
 | 
					* `deleteLabel`
 | 
				
			||||||
 | 
					* `deleteOrganizationInvitation`
 | 
				
			||||||
 | 
					* `disablePlugin`
 | 
				
			||||||
 | 
					* `disablePowerUp`
 | 
				
			||||||
 | 
					* `emailCard`
 | 
				
			||||||
 | 
					* `enablePlugin`
 | 
				
			||||||
 | 
					* `enablePowerUp`
 | 
				
			||||||
 | 
					* `makeAdminOfBoard`
 | 
				
			||||||
 | 
					* `makeAdminOfOrganization`
 | 
				
			||||||
 | 
					* `makeNormalMemberOfBoard`
 | 
				
			||||||
 | 
					* `makeNormalMemberOfOrganization`
 | 
				
			||||||
 | 
					* `makeObserverOfBoard`
 | 
				
			||||||
 | 
					* `memberJoinedTrello`
 | 
				
			||||||
 | 
					* `moveCardFromBoard`
 | 
				
			||||||
 | 
					* `moveCardToBoard`
 | 
				
			||||||
 | 
					* `moveListFromBoard`
 | 
				
			||||||
 | 
					* `moveListToBoard`
 | 
				
			||||||
 | 
					* `removeAdminFromBoard`
 | 
				
			||||||
 | 
					* `removeAdminFromOrganization`
 | 
				
			||||||
 | 
					* `removeBoardsPinnedFromMember`
 | 
				
			||||||
 | 
					* `removeFromOrganizationBoard`
 | 
				
			||||||
 | 
					* `removeLabelFromCard`
 | 
				
			||||||
 | 
					* `removeMemberFromBoard`
 | 
				
			||||||
 | 
					* `removeMemberFromOrganization`
 | 
				
			||||||
 | 
					* `unconfirmedBoardInvitation`
 | 
				
			||||||
 | 
					* `unconfirmedOrganizationInvitation`
 | 
				
			||||||
 | 
					* `updateBoard`
 | 
				
			||||||
 | 
					* `updateCheckItem`
 | 
				
			||||||
 | 
					* `updateChecklist`
 | 
				
			||||||
 | 
					* `updateLabel`
 | 
				
			||||||
 | 
					* `updateMember`
 | 
				
			||||||
 | 
					* `updateOrganization`
 | 
				
			||||||
 | 
					* `voteOnCard`
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								example-alert.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								example-alert.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 33 KiB  | 
							
								
								
									
										136
									
								
								package-lock.json
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								package-lock.json
									
										
									
										generated
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,136 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "name": "trellobot",
 | 
				
			||||||
 | 
					  "version": "1.0.0",
 | 
				
			||||||
 | 
					  "lockfileVersion": 1,
 | 
				
			||||||
 | 
					  "requires": true,
 | 
				
			||||||
 | 
					  "dependencies": {
 | 
				
			||||||
 | 
					    "async-limiter": {
 | 
				
			||||||
 | 
					      "version": "1.0.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "coffee-script": {
 | 
				
			||||||
 | 
					      "version": "1.3.2",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.2.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha1-Lg0rgjQiB3sPXLDKXJuSTUytB1g="
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "discord.js": {
 | 
				
			||||||
 | 
					      "version": "11.3.2",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-11.3.2.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-Abw9CTMX3Jb47IeRffqx2VNSnXl/OsTdQzhvbw/JnqCyqc2imAocc7pX2HoRmgKd8CgSqsjBFBneusz/E16e6A==",
 | 
				
			||||||
 | 
					      "requires": {
 | 
				
			||||||
 | 
					        "long": "^4.0.0",
 | 
				
			||||||
 | 
					        "prism-media": "^0.0.2",
 | 
				
			||||||
 | 
					        "snekfetch": "^3.6.4",
 | 
				
			||||||
 | 
					        "tweetnacl": "^1.0.0",
 | 
				
			||||||
 | 
					        "ws": "^4.0.0"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "extend": {
 | 
				
			||||||
 | 
					      "version": "1.3.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/extend/-/extend-1.3.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha1-0VFvsP9WJNLr+RI+odrFoZlABPg="
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "long": {
 | 
				
			||||||
 | 
					      "version": "4.0.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node-trello": {
 | 
				
			||||||
 | 
					      "version": "0.1.5",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/node-trello/-/node-trello-0.1.5.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha1-rGWDVYn7iXLTS6nJXbKLcnoYNpQ=",
 | 
				
			||||||
 | 
					      "requires": {
 | 
				
			||||||
 | 
					        "coffee-script": "1.3.2",
 | 
				
			||||||
 | 
					        "oauth": "0.9.7",
 | 
				
			||||||
 | 
					        "request": "2.12.0"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "oauth": {
 | 
				
			||||||
 | 
					      "version": "0.9.7",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.7.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha1-wlVNA2jJZuswUL7JZYRiVXetHs0="
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "prism-media": {
 | 
				
			||||||
 | 
					      "version": "0.0.2",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-0.0.2.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-L6yc8P5NVG35ivzvfI7bcTYzqFV+K8gTfX9YaJbmIFfMXTs71RMnAupvTQPTCteGsiOy9QcNLkQyWjAafY/hCQ=="
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "request": {
 | 
				
			||||||
 | 
					      "version": "2.12.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/request/-/request-2.12.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha1-EfRvILPQ9ISMY4OZHIB5CvFsjkg=",
 | 
				
			||||||
 | 
					      "requires": {
 | 
				
			||||||
 | 
					        "form-data": "~0.0.3",
 | 
				
			||||||
 | 
					        "mime": "~1.2.7"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "form-data": {
 | 
				
			||||||
 | 
					          "version": "0.0.3",
 | 
				
			||||||
 | 
					          "bundled": true,
 | 
				
			||||||
 | 
					          "requires": {
 | 
				
			||||||
 | 
					            "async": "~0.1.9",
 | 
				
			||||||
 | 
					            "combined-stream": "0.0.3",
 | 
				
			||||||
 | 
					            "mime": "~1.2.2"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "dependencies": {
 | 
				
			||||||
 | 
					            "async": {
 | 
				
			||||||
 | 
					              "version": "0.1.9",
 | 
				
			||||||
 | 
					              "bundled": true
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "combined-stream": {
 | 
				
			||||||
 | 
					              "version": "0.0.3",
 | 
				
			||||||
 | 
					              "bundled": true,
 | 
				
			||||||
 | 
					              "requires": {
 | 
				
			||||||
 | 
					                "delayed-stream": "0.0.5"
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					              "dependencies": {
 | 
				
			||||||
 | 
					                "delayed-stream": {
 | 
				
			||||||
 | 
					                  "version": "0.0.5",
 | 
				
			||||||
 | 
					                  "bundled": true
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "mime": {
 | 
				
			||||||
 | 
					          "version": "1.2.7",
 | 
				
			||||||
 | 
					          "bundled": true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "safe-buffer": {
 | 
				
			||||||
 | 
					      "version": "5.1.2",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "snekfetch": {
 | 
				
			||||||
 | 
					      "version": "3.6.4",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/snekfetch/-/snekfetch-3.6.4.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-NjxjITIj04Ffqid5lqr7XdgwM7X61c/Dns073Ly170bPQHLm6jkmelye/eglS++1nfTWktpP6Y2bFXjdPlQqdw=="
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "trello-events": {
 | 
				
			||||||
 | 
					      "version": "0.1.6",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/trello-events/-/trello-events-0.1.6.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha1-hDQckGPU4SDHq0uwbXkT1/vGl9o=",
 | 
				
			||||||
 | 
					      "requires": {
 | 
				
			||||||
 | 
					        "extend": "^1.2.1",
 | 
				
			||||||
 | 
					        "node-trello": "^0.1.4"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "tweetnacl": {
 | 
				
			||||||
 | 
					      "version": "1.0.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha1-cT2LgY2kIGh0C/aDhtBHnmb8ins="
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "ws": {
 | 
				
			||||||
 | 
					      "version": "4.1.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-ZGh/8kF9rrRNffkLFV4AzhvooEclrOH0xaugmqGsIfFgOE/pIz4fMc4Ef+5HSQqTEug2S9JZIWDR47duDSLfaA==",
 | 
				
			||||||
 | 
					      "requires": {
 | 
				
			||||||
 | 
					        "async-limiter": "~1.0.0",
 | 
				
			||||||
 | 
					        "safe-buffer": "~5.1.0"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										29
									
								
								package.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								package.json
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,29 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "name": "trellobot",
 | 
				
			||||||
 | 
					  "version": "1.0.0",
 | 
				
			||||||
 | 
					  "description": "A Discord bot for logging Trello events.",
 | 
				
			||||||
 | 
					  "main": "trellobot.js",
 | 
				
			||||||
 | 
					  "scripts": {
 | 
				
			||||||
 | 
					    "start": "node trellobot.js"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "repository": {
 | 
				
			||||||
 | 
					    "type": "git",
 | 
				
			||||||
 | 
					    "url": "git+https://github.com/Angush/trellobot.git"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "author": "Angush",
 | 
				
			||||||
 | 
					  "license": "ISC",
 | 
				
			||||||
 | 
					  "bugs": {
 | 
				
			||||||
 | 
					    "url": "https://github.com/Angush/trellobot/issues"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "homepage": "https://github.com/Angush/trellobot#readme",
 | 
				
			||||||
 | 
					  "dependencies": {
 | 
				
			||||||
 | 
					    "discord.js": "^11.3.2",
 | 
				
			||||||
 | 
					    "trello-events": "^0.1.6"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "nodemonConfig": {
 | 
				
			||||||
 | 
					    "ignore": [
 | 
				
			||||||
 | 
					      "*.md",
 | 
				
			||||||
 | 
					      ".latestActivityID"
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										43
									
								
								readme.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								readme.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,43 @@
 | 
				
			||||||
 | 
					# Trellobot
 | 
				
			||||||
 | 
					A simple Discord bot to log and report events from your Trello boards in your Discord server.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Setup
 | 
				
			||||||
 | 
					1. Clone repository.
 | 
				
			||||||
 | 
					2. Run `npm install`.
 | 
				
			||||||
 | 
					3. Configure `conf.json` file as desired ([see below](#confjson)).
 | 
				
			||||||
 | 
					4. Generate tokens and set up `.auth` file ([see below](#auth)).
 | 
				
			||||||
 | 
					5. All done. Run Trellobot with `node trellobot.js`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## conf.json
 | 
				
			||||||
 | 
					There are several important values in here which inform Trellobot's operation. Here's what they're all for, and how to set them.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*(optional properties marked with * asterisks)*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Property         | Explanation
 | 
				
			||||||
 | 
					---------------- | -----------
 | 
				
			||||||
 | 
					`boardIDs`       | An array of board IDs (strings) determining on which boards Trellobot reports. IDs can be extracted from the URLs of your Trello boards. (eg. the board ID for [https://trello.com/b/**HF8XAoZd**/welcome-board](https://trello.com/b/HF8XAoZd/welcome-board) is `HF8XAoZd`).
 | 
				
			||||||
 | 
					`serverID`       | An ID string determining which Discord server Trellobot uses. Enable developer mode in Discord and right click a server icon to copy its ID.
 | 
				
			||||||
 | 
					`channelID`      | An ID string determining which channel on your Discord server Trellobot uses to post reports. Enable developer mode in Discord and right click a channel to copy its ID.
 | 
				
			||||||
 | 
					`pollInterval`   | An integer determining how often (in milliseconds) Trellobot polls your boards for activity. 
 | 
				
			||||||
 | 
					`prefix`*        | A string determining the prefix for Trellobot commands in Discord. Currently unused. Defaults to `.` (period).
 | 
				
			||||||
 | 
					`contentString`* | A string included posted alongside all embeds. If you'd like to ping a certain role every time the bot posts, for example, you would put that string here.
 | 
				
			||||||
 | 
					`enabledEvents`* | An array of event names (strings) determining whitelisted events (ie. which events will be reported; if empty, all events are enabled). Eligible event names can be found [in the `events.md` file](https://github.com/angush/trellobot/blob/master/events.md).
 | 
				
			||||||
 | 
					`userIDs`*       | An object mapping Discord IDs to Trello usernames, like so: `userIDs: {"TrelloUser": "1395184357104955", ...}`, so Trellobot can pull relevant user data from Discord.
 | 
				
			||||||
 | 
					`realNames`*     | A boolean (defaulting to true) that determines whether Trellobot uses the full names or usernames from Trello (eg. `John Smith` vs `jsmiff2`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You can refer to the `conf.json` included in the repository for an example.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## .auth
 | 
				
			||||||
 | 
					The `.auth` file is included as a template to save you time, but you will need to create the keys and tokens yourself to run Trellobot. Here's how:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Property       | How to get the value
 | 
				
			||||||
 | 
					-------------- | ----------------------
 | 
				
			||||||
 | 
					`discordToken` | Create an app for Trellobot to work through on [Discord's developer site](https://discordapp.com/developers/applications/me/create), then create a bot user (below app description/icon) and copy the token.
 | 
				
			||||||
 | 
					`trelloKey`    | Visit [this page](https://trello.com/1/appKey/generate) to generate your public Trello API key.
 | 
				
			||||||
 | 
					`trelloToken`  | Visit `https://trello.com/1/connect?name=Trellobot&response_type=token&expiration=never&key=YOURPUBLICKEY` (replacing `YOURPUBLICKEY` with the appropriate key) to generate a token that does not expire. Remove `&expiration=never` from the URL if you'd prefer a temporary token.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					That's all for now.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*i know the name is lame*
 | 
				
			||||||
							
								
								
									
										359
									
								
								trellobot.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										359
									
								
								trellobot.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,359 @@
 | 
				
			||||||
 | 
					const Discord = require('discord.js')
 | 
				
			||||||
 | 
					const bot = new Discord.Client()
 | 
				
			||||||
 | 
					const fs = require('fs')
 | 
				
			||||||
 | 
					const auth = JSON.parse(fs.readFileSync('.auth'))
 | 
				
			||||||
 | 
					const conf = JSON.parse(fs.readFileSync('conf.json'))
 | 
				
			||||||
 | 
					let latestActivityID = fs.existsSync('.latestActivityID') ? fs.readFileSync('.latestActivityID') : 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Trello = require('trello-events')
 | 
				
			||||||
 | 
					const events = new Trello({
 | 
				
			||||||
 | 
					    pollFrequency: conf.pollInterval, // milliseconds
 | 
				
			||||||
 | 
					    minId: latestActivityID, // auto-created and auto-updated
 | 
				
			||||||
 | 
					    start: false,
 | 
				
			||||||
 | 
					    trello: {
 | 
				
			||||||
 | 
					        boards: conf.boardIDs, // array of Trello board IDs 
 | 
				
			||||||
 | 
					        key: auth.trelloKey, // your public Trello API key
 | 
				
			||||||
 | 
					        token: auth.trelloToken // your private Trello token for Trellobot
 | 
				
			||||||
 | 
					    } 
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					** =====================================
 | 
				
			||||||
 | 
					** Discord event handlers and functions.
 | 
				
			||||||
 | 
					** =====================================
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bot.login(auth.discordToken)
 | 
				
			||||||
 | 
					bot.on('ready', () => {
 | 
				
			||||||
 | 
					    let guild = bot.guilds.get(conf.serverID)
 | 
				
			||||||
 | 
					    let channel = bot.channels.get(conf.channelID)
 | 
				
			||||||
 | 
					    if (!guild) {
 | 
				
			||||||
 | 
					        console.log(`Server with ID "${conf.serverID}" not found! I can't function without a valid server and channel.\nPlease add the correct server ID to your conf file, or if the conf data is correct, ensure I have proper access.\nYou may need to add me to your server using this link:\n    https://discordapp.com/api/oauth2/authorize?client_id=${bot.user.id}&permissions=0&scope=bot`)
 | 
				
			||||||
 | 
					        process.exit()
 | 
				
			||||||
 | 
					    } else if (!channel) {
 | 
				
			||||||
 | 
					        console.log(`Channel with ID "${conf.channelID}" not found! I can't function without a valid channel.\nPlease add the correct channel ID to your conf file, or if the conf data is correct, ensure I have proper access.`)
 | 
				
			||||||
 | 
					        process.exit()
 | 
				
			||||||
 | 
					    } else if (!conf.boardIDs || conf.boardIDs.length < 1) {
 | 
				
			||||||
 | 
					        console.log(`No board IDs provided! Please add at least one to your conf file. Check the readme if you need help finding a board ID.`)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    conf.guild = guild
 | 
				
			||||||
 | 
					    conf.channel = channel
 | 
				
			||||||
 | 
					    /* 
 | 
				
			||||||
 | 
					    ** Make contentString a map of event names to their paired strings
 | 
				
			||||||
 | 
					    ** like this: {"createCard": "someone created a card", ...}, so you
 | 
				
			||||||
 | 
					    ** can, for example, ping specific roles for specific events.
 | 
				
			||||||
 | 
					    **
 | 
				
			||||||
 | 
					    ** Also add a new conf section for pairing lists within a board to 
 | 
				
			||||||
 | 
					    ** contentStrings? That way you can ping one role for new Moderation
 | 
				
			||||||
 | 
					    ** cards, and another role for new Event cards, for example.
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    if (!conf.contentString) conf.contentString = "" 
 | 
				
			||||||
 | 
					    if (!conf.enabledEvents) conf.enabledEvents = []
 | 
				
			||||||
 | 
					    if (!conf.userIDs) conf.userIDs = {}
 | 
				
			||||||
 | 
					    if (!conf.realNames) conf.realNames = true
 | 
				
			||||||
 | 
					    // set default prefix is none provided in conf
 | 
				
			||||||
 | 
					    if (!conf.prefix) {
 | 
				
			||||||
 | 
					        conf.prefix = "."
 | 
				
			||||||
 | 
					        fs.writeFileSync('conf.json', JSON.stringify(conf, null, 4), (err, data) => console.log(`Updated conf file with default prefix ('.')`))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // logInitializationData()
 | 
				
			||||||
 | 
					    console.log(`== Bot logged in as @${bot.user.tag}. Ready for action! ==`)
 | 
				
			||||||
 | 
					    events.start()
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bot.on('message', (msg) => {
 | 
				
			||||||
 | 
					    if (msg.channel.type !== "text") return
 | 
				
			||||||
 | 
					    if (msg.content.startsWith(`${conf.prefix}ping`)) {
 | 
				
			||||||
 | 
					        let now = Date.now()
 | 
				
			||||||
 | 
					        msg.channel.send(`Ping!`).then(m => {
 | 
				
			||||||
 | 
					            m.edit(`Pong! (took ${Date.now() - now}ms)`)
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					** ====================================
 | 
				
			||||||
 | 
					** Trello event handlers and functions.
 | 
				
			||||||
 | 
					** ====================================
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Fired when a card is created
 | 
				
			||||||
 | 
					events.on('createCard', (event, board) => {
 | 
				
			||||||
 | 
					    if (!eventEnabled(`cardCreated`)) return
 | 
				
			||||||
 | 
					    let embed = getEmbedBase(event)
 | 
				
			||||||
 | 
					        .setTitle(`New card created under __${event.data.list.name}__!`)
 | 
				
			||||||
 | 
					        .setDescription(`**CARD:** ${event.data.card.name} — **[CARD LINK](https://trello.com/c/${event.data.card.shortLink})**\n\n**EVENT:** Card created under __${event.data.list.name}__ by **[${conf.realNames ? event.memberCreator.fullName : event.memberCreator.username}](https://trello.com/${event.memberCreator.username})**`)
 | 
				
			||||||
 | 
					    send(addDiscordUserData(embed, event.memberCreator))
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Fired when a card is updated (description, due date, position, associated list, name, and archive status)
 | 
				
			||||||
 | 
					events.on('updateCard', (event, board) => {
 | 
				
			||||||
 | 
					    let embed = getEmbedBase(event)
 | 
				
			||||||
 | 
					    if (event.data.old.hasOwnProperty("desc")) {
 | 
				
			||||||
 | 
					        if (!eventEnabled(`cardDescriptionChanged`)) return
 | 
				
			||||||
 | 
					        embed
 | 
				
			||||||
 | 
					            .setTitle(`Card description changed!`)
 | 
				
			||||||
 | 
					            .setDescription(`**CARD:** ${event.data.card.name} — **[CARD LINK](https://trello.com/c/${event.data.card.shortLink})**\n\n**EVENT:** Card description changed (see below) by **[${conf.realNames ? event.memberCreator.fullName : event.memberCreator.username}](https://trello.com/${event.memberCreator.username})**`)
 | 
				
			||||||
 | 
					            .addField(`New Description`, typeof event.data.card.desc === "string" && event.data.card.desc.trim().length > 0 ? (event.data.card.desc.length > 1024 ? `${event.data.card.desc.trim().slice(0, 1020)}...` : event.data.card.desc) : `*[No description]*`)
 | 
				
			||||||
 | 
					            .addField(`Old Description`, typeof event.data.old.desc === "string" && event.data.old.desc.trim().length > 0 ? (event.data.old.desc.length > 1024 ? `${event.data.old.desc.trim().slice(0, 1020)}...` : event.data.old.desc) : `*[No description]*`)
 | 
				
			||||||
 | 
					        send(addDiscordUserData(embed, event.memberCreator))
 | 
				
			||||||
 | 
					    } else if (event.data.old.hasOwnProperty("due")) {
 | 
				
			||||||
 | 
					        if (!eventEnabled(`cardDueDateChanged`)) return
 | 
				
			||||||
 | 
					        embed
 | 
				
			||||||
 | 
					            .setTitle(`Card due date changed!`)
 | 
				
			||||||
 | 
					            .setDescription(`**CARD:** ${event.data.card.name} — **[CARD LINK](https://trello.com/c/${event.data.card.shortLink})**\n\n**EVENT:** Card due date changed to __${event.data.card.due ? new Date(event.data.card.due).toUTCString() : `[No due date]`}__ from __${event.data.old.due ? new Date(event.data.old.due).toUTCString() : `[No due date]`}__ by **[${conf.realNames ? event.memberCreator.fullName : event.memberCreator.username}](https://trello.com/${event.memberCreator.username})**`)
 | 
				
			||||||
 | 
					        send(addDiscordUserData(embed, event.memberCreator))
 | 
				
			||||||
 | 
					    } else if (event.data.old.hasOwnProperty("pos")) {
 | 
				
			||||||
 | 
					        if (!eventEnabled(`cardPositionChanged`)) return
 | 
				
			||||||
 | 
					        embed
 | 
				
			||||||
 | 
					            .setTitle(`Card position changed!`)
 | 
				
			||||||
 | 
					            .setDescription(`**CARD:** ${event.data.card.name} — **[CARD LINK](https://trello.com/c/${event.data.card.shortLink})**\n\n**EVENT:** Card position in list __${event.data.list.name}__ changed by **[${conf.realNames ? event.memberCreator.fullName : event.memberCreator.username}](https://trello.com/${event.memberCreator.username})**`)
 | 
				
			||||||
 | 
					        send(addDiscordUserData(embed, event.memberCreator))
 | 
				
			||||||
 | 
					    } else if (event.data.old.hasOwnProperty("idList")) {
 | 
				
			||||||
 | 
					        if (!eventEnabled(`cardListChanged`)) return
 | 
				
			||||||
 | 
					        embed
 | 
				
			||||||
 | 
					            .setTitle(`Card list changed!`)
 | 
				
			||||||
 | 
					            .setDescription(`**CARD:** ${event.data.card.name} — **[CARD LINK](https://trello.com/c/${event.data.card.shortLink})**\n\n**EVENT:** Card moved to list __${event.data.listAfter.name}__ from list __${event.data.listBefore.name}__ by **[${conf.realNames ? event.memberCreator.fullName : event.memberCreator.username}](https://trello.com/${event.memberCreator.username})**`)
 | 
				
			||||||
 | 
					        send(addDiscordUserData(embed, event.memberCreator))
 | 
				
			||||||
 | 
					    } else if (event.data.old.hasOwnProperty("name")) {
 | 
				
			||||||
 | 
					        if (!eventEnabled(`cardNameChanged`)) return
 | 
				
			||||||
 | 
					        embed
 | 
				
			||||||
 | 
					            .setTitle(`Card name changed!`)
 | 
				
			||||||
 | 
					            .setDescription(`**CARD:** *[See below for card name]* — **[CARD LINK](https://trello.com/c/${event.data.card.shortLink})**\n\n**EVENT:** Card name changed (see below) by **[${conf.realNames ? event.memberCreator.fullName : event.memberCreator.username}](https://trello.com/${event.memberCreator.username})**`)
 | 
				
			||||||
 | 
					            .addField(`New Name`, event.data.card.name)
 | 
				
			||||||
 | 
					            .addField(`Old Name`, event.data.old.name)
 | 
				
			||||||
 | 
					        send(addDiscordUserData(embed, event.memberCreator))
 | 
				
			||||||
 | 
					    } else if (event.data.old.hasOwnProperty("closed")) {
 | 
				
			||||||
 | 
					        if (event.data.old.closed) {
 | 
				
			||||||
 | 
					            if (!eventEnabled(`cardUnarchived`)) return
 | 
				
			||||||
 | 
					            embed
 | 
				
			||||||
 | 
					                .setTitle(`Card unarchived!`)
 | 
				
			||||||
 | 
					                .setDescription(`**CARD:** ${event.data.card.name} — **[CARD LINK](https://trello.com/c/${event.data.card.shortLink})**\n\n**EVENT:** Card unarchived and returned to list __${event.data.list.name}__ by **[${conf.realNames ? event.memberCreator.fullName : event.memberCreator.username}](https://trello.com/${event.memberCreator.username})**`)
 | 
				
			||||||
 | 
					            send(addDiscordUserData(embed, event.memberCreator))
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            if (!eventEnabled(`cardArchived`)) return
 | 
				
			||||||
 | 
					            embed
 | 
				
			||||||
 | 
					                .setTitle(`Card archived!`)
 | 
				
			||||||
 | 
					                .setDescription(`**CARD:** ${event.data.card.name} — **[CARD LINK](https://trello.com/c/${event.data.card.shortLink})**\n\n**EVENT:** Card under list __${event.data.list.name}__ archived by **[${conf.realNames ? event.memberCreator.fullName : event.memberCreator.username}](https://trello.com/${event.memberCreator.username})**`)
 | 
				
			||||||
 | 
					            send(addDiscordUserData(embed, event.memberCreator))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Fired when a card is deleted
 | 
				
			||||||
 | 
					events.on('deleteCard', (event, board) => {
 | 
				
			||||||
 | 
					    if (!eventEnabled(`cardDeleted`)) return
 | 
				
			||||||
 | 
					    let embed = getEmbedBase(event)
 | 
				
			||||||
 | 
					        .setTitle(`Card deleted!`)
 | 
				
			||||||
 | 
					        .setDescription(`**EVENT:** Card deleted from list __${event.data.list.name}__ by **[${conf.realNames ? event.memberCreator.fullName : event.memberCreator.username}](https://trello.com/${event.memberCreator.username})**`)
 | 
				
			||||||
 | 
					    send(addDiscordUserData(embed, event.memberCreator))
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Fired when a comment is posted, or edited
 | 
				
			||||||
 | 
					events.on('commentCard', (event, board) => {
 | 
				
			||||||
 | 
					    let embed = getEmbedBase(event)
 | 
				
			||||||
 | 
					    if (event.data.hasOwnProperty("textData")) {
 | 
				
			||||||
 | 
					        if (!eventEnabled(`commentEdited`)) return
 | 
				
			||||||
 | 
					        embed
 | 
				
			||||||
 | 
					            .setTitle(`Comment edited on card!`)
 | 
				
			||||||
 | 
					            .setDescription(`**CARD:** ${event.data.card.name} — **[CARD LINK](https://trello.com/c/${event.data.card.shortLink})**\n\n**EVENT:** Card comment edited (see below for comment text) by **[${conf.realNames ? event.memberCreator.fullName : event.memberCreator.username}](https://trello.com/${event.memberCreator.username})**`)
 | 
				
			||||||
 | 
					            .addField(`Comment Text`, event.data.text.length > 1024 ? `${event.data.text.trim().slice(0, 1020)}...` : event.data.text)
 | 
				
			||||||
 | 
					            .setTimestamp(event.data.dateLastEdited)
 | 
				
			||||||
 | 
					        send(addDiscordUserData(embed, event.memberCreator))
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        if (!eventEnabled(`commentAdded`)) return
 | 
				
			||||||
 | 
					        embed
 | 
				
			||||||
 | 
					            .setTitle(`Comment added to card!`)
 | 
				
			||||||
 | 
					            .setDescription(`**CARD:** ${event.data.card.name} — **[CARD LINK](https://trello.com/c/${event.data.card.shortLink})**\n\n**EVENT:** Card comment added (see below for comment text) by **[${conf.realNames ? event.memberCreator.fullName : event.memberCreator.username}](https://trello.com/${event.memberCreator.username})**`)
 | 
				
			||||||
 | 
					            .addField(`Comment Text`, event.data.text.length > 1024 ? `${event.data.text.trim().slice(0, 1020)}...` : event.data.text)
 | 
				
			||||||
 | 
					        send(addDiscordUserData(embed, event.memberCreator))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Fired when a member is added to a card
 | 
				
			||||||
 | 
					events.on('addMemberToCard', (event, board) => {
 | 
				
			||||||
 | 
					    let embed = getEmbedBase(event)
 | 
				
			||||||
 | 
					        .setTitle(`Member added to card!`)
 | 
				
			||||||
 | 
					        .setDescription(`**CARD:** ${event.data.card.name} — **[CARD LINK](https://trello.com/c/${event.data.card.shortLink})**\n\n**EVENT:** Member **[${conf.realNames ? event.member.fullName : event.member.username}](https://trello.com/${event.member.username})**`)
 | 
				
			||||||
 | 
					    let editedEmbed = addDiscordUserData(embed, event.member)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (event.member.id === event.memberCreator.id) {
 | 
				
			||||||
 | 
					        if (!eventEnabled(`memberAddedToCardBySelf`)) return
 | 
				
			||||||
 | 
					        editedEmbed.setDescription(editedEmbed.description + ` added themselves to card.`)
 | 
				
			||||||
 | 
					        send(editedEmbed)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        if (!eventEnabled(`memberAddedToCard`)) return
 | 
				
			||||||
 | 
					        editedEmbed.setDescription(editedEmbed.description + ` added to card by **[${conf.realNames ? event.memberCreator.fullName : event.memberCreator.username}](https://trello.com/${event.memberCreator.username})**`)
 | 
				
			||||||
 | 
					        send(addDiscordUserData(editedEmbed, event.memberCreator))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Fired when a member is removed from a card
 | 
				
			||||||
 | 
					events.on('removeMemberFromCard', (event, board) => {
 | 
				
			||||||
 | 
					    let embed = getEmbedBase(event)
 | 
				
			||||||
 | 
					        .setTitle(`Member removed from card!`)
 | 
				
			||||||
 | 
					        .setDescription(`**CARD:** ${event.data.card.name} — **[CARD LINK](https://trello.com/c/${event.data.card.shortLink})**\n\n**EVENT:** Member **[${conf.realNames ? event.member.fullName : event.member.username}](https://trello.com/${event.member.username})**`)
 | 
				
			||||||
 | 
					    let editedEmbed = addDiscordUserData(embed, event.member)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (event.member.id === event.memberCreator.id) {
 | 
				
			||||||
 | 
					        if (!eventEnabled(`memberRemovedFromCardBySelf`)) return
 | 
				
			||||||
 | 
					        editedEmbed.setDescription(editedEmbed.description + ` removed themselves from card.`)
 | 
				
			||||||
 | 
					        send(editedEmbed)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        if (!eventEnabled(`memberRemovedFromCard`)) return
 | 
				
			||||||
 | 
					        editedEmbed.setDescription(editedEmbed.description + ` removed from card by **[${conf.realNames ? event.memberCreator.fullName : event.memberCreator.username}](https://trello.com/${event.memberCreator.username})**`)
 | 
				
			||||||
 | 
					        send(addDiscordUserData(editedEmbed, event.memberCreator))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Fired when a list is created
 | 
				
			||||||
 | 
					events.on('createList', (event, board) => {
 | 
				
			||||||
 | 
					    if (!eventEnabled(`listCreated`)) return
 | 
				
			||||||
 | 
					    let embed = getEmbedBase(event)
 | 
				
			||||||
 | 
					        .setTitle(`New list created!`)
 | 
				
			||||||
 | 
					        .setDescription(`**EVENT:** List __${event.data.list.name}__ created by **[${conf.realNames ? event.memberCreator.fullName : event.memberCreator.username}](https://trello.com/${event.memberCreator.username})**`)
 | 
				
			||||||
 | 
					    send(addDiscordUserData(embed, event.memberCreator))
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Fired when a list is renamed, moved, archived, or unarchived
 | 
				
			||||||
 | 
					events.on('updateList', (event, board) => {
 | 
				
			||||||
 | 
					    let embed = getEmbedBase(event)
 | 
				
			||||||
 | 
					    if (event.data.old.hasOwnProperty("name")) {
 | 
				
			||||||
 | 
					        if (!eventEnabled(`listNameChanged`)) return
 | 
				
			||||||
 | 
					        embed
 | 
				
			||||||
 | 
					            .setTitle(`List name changed!`)
 | 
				
			||||||
 | 
					            .setDescription(`**EVENT:** List renamed to __${event.data.list.name}__ from __${event.data.old.name}__ by **[${conf.realNames ? event.memberCreator.fullName : event.memberCreator.username}](https://trello.com/${event.memberCreator.username})**`)
 | 
				
			||||||
 | 
					        send(addDiscordUserData(embed, event.memberCreator))
 | 
				
			||||||
 | 
					    } else if (event.data.old.hasOwnProperty("pos")) {
 | 
				
			||||||
 | 
					        if (!eventEnabled(`listPositionChanged`)) return
 | 
				
			||||||
 | 
					        embed
 | 
				
			||||||
 | 
					            .setTitle(`List position changed!`)
 | 
				
			||||||
 | 
					            .setDescription(`**EVENT:** List __${event.data.list.name}__ position changed by **[${conf.realNames ? event.memberCreator.fullName : event.memberCreator.username}](https://trello.com/${event.memberCreator.username})**`)
 | 
				
			||||||
 | 
					        send(addDiscordUserData(embed, event.memberCreator))
 | 
				
			||||||
 | 
					    } else if (event.data.old.hasOwnProperty("closed")) {
 | 
				
			||||||
 | 
					        if (event.data.old.closed) {
 | 
				
			||||||
 | 
					            if (!eventEnabled(`listUnarchived`)) return
 | 
				
			||||||
 | 
					            embed
 | 
				
			||||||
 | 
					                .setTitle(`List unarchived!`)
 | 
				
			||||||
 | 
					                .setDescription(`**EVENT:** List __${event.data.list.name}__ unarchived by **[${conf.realNames ? event.memberCreator.fullName : event.memberCreator.username}](https://trello.com/${event.memberCreator.username})**`)
 | 
				
			||||||
 | 
					            send(addDiscordUserData(embed, event.memberCreator))
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            if (!eventEnabled(`listArchived`)) return
 | 
				
			||||||
 | 
					            embed
 | 
				
			||||||
 | 
					                .setTitle(`List archived!`)
 | 
				
			||||||
 | 
					                .setDescription(`**EVENT:** List __${event.data.list.name}__ archived by **[${conf.realNames ? event.memberCreator.fullName : event.memberCreator.username}](https://trello.com/${event.memberCreator.username})**`)
 | 
				
			||||||
 | 
					            send(addDiscordUserData(embed, event.memberCreator))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Fired when an attachment is added to a card
 | 
				
			||||||
 | 
					events.on('addAttachmentToCard', (event, board) => {
 | 
				
			||||||
 | 
					    if (!eventEnabled(`attachmentAddedToCard`)) return
 | 
				
			||||||
 | 
					    let embed = getEmbedBase(event)
 | 
				
			||||||
 | 
					        .setTitle(`Attachment added to card!`)
 | 
				
			||||||
 | 
					        .setDescription(`**CARD:** ${event.data.card.name} — **[CARD LINK](https://trello.com/c/${event.data.card.shortLink})**\n\n**EVENT:** Attachment named \`${event.data.attachment.name}\` added to card by **[${conf.realNames ? event.memberCreator.fullName : event.memberCreator.username}](https://trello.com/${event.memberCreator.username})**`)
 | 
				
			||||||
 | 
					    send(addDiscordUserData(embed, event.memberCreator))
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Fired when an attachment is removed from a card
 | 
				
			||||||
 | 
					events.on('deleteAttachmentFromCard', (event, board) => {
 | 
				
			||||||
 | 
					    if (!eventEnabled(`attachmentRemovedFromCard`)) return
 | 
				
			||||||
 | 
					    let embed = getEmbedBase(event)
 | 
				
			||||||
 | 
					        .setTitle(`Attachment removed from card!`)
 | 
				
			||||||
 | 
					        .setDescription(`**CARD:** ${event.data.card.name} — **[CARD LINK](https://trello.com/c/${event.data.card.shortLink})**\n\n**EVENT:** Attachment named \`${event.data.attachment.name}\` removed from card by **[${conf.realNames ? event.memberCreator.fullName : event.memberCreator.username}](https://trello.com/${event.memberCreator.username})**`)
 | 
				
			||||||
 | 
					    send(addDiscordUserData(embed, event.memberCreator))
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Fired when a checklist is added to a card (same thing as created)
 | 
				
			||||||
 | 
					events.on('addChecklistToCard', (event, board) => {
 | 
				
			||||||
 | 
					    if (!eventEnabled(`checklistAddedToCard`)) return
 | 
				
			||||||
 | 
					    let embed = getEmbedBase(event)
 | 
				
			||||||
 | 
					        .setTitle(`Checklist added to card!`)
 | 
				
			||||||
 | 
					        .setDescription(`**CARD:** ${event.data.card.name} — **[CARD LINK](https://trello.com/c/${event.data.card.shortLink})**\n\n**EVENT:** Checklist named \`${event.data.checklist.name}\` added to card by **[${conf.realNames ? event.memberCreator.fullName : event.memberCreator.username}](https://trello.com/${event.memberCreator.username})**`)
 | 
				
			||||||
 | 
					    send(addDiscordUserData(embed, event.memberCreator))
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Fired when a checklist is removed from a card (same thing as deleted)
 | 
				
			||||||
 | 
					events.on('removeChecklistFromCard', (event, board) => {
 | 
				
			||||||
 | 
					    if (!eventEnabled(`checklistRemovedFromCard`)) return
 | 
				
			||||||
 | 
					    let embed = getEmbedBase(event)
 | 
				
			||||||
 | 
					        .setTitle(`Checklist removed from card!`)
 | 
				
			||||||
 | 
					        .setDescription(`**CARD:** ${event.data.card.name} — **[CARD LINK](https://trello.com/c/${event.data.card.shortLink})**\n\n**EVENT:** Checklist named \`${event.data.checklist.name}\` removed from card by **[${conf.realNames ? event.memberCreator.fullName : event.memberCreator.username}](https://trello.com/${event.memberCreator.username})**`)
 | 
				
			||||||
 | 
					    send(addDiscordUserData(embed, event.memberCreator))
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Fired when a checklist item's completion status is toggled
 | 
				
			||||||
 | 
					events.on('updateCheckItemStateOnCard', (event, board) => {
 | 
				
			||||||
 | 
					    if (event.data.checkItem.state === "complete") {
 | 
				
			||||||
 | 
					        if (!eventEnabled(`checklistItemMarkedComplete`)) return
 | 
				
			||||||
 | 
					        let embed = getEmbedBase(event)
 | 
				
			||||||
 | 
					            .setTitle(`Checklist item marked complete!`)
 | 
				
			||||||
 | 
					            .setDescription(`**CARD:** ${event.data.card.name} — **[CARD LINK](https://trello.com/c/${event.data.card.shortLink})**\n\n**EVENT:** Checklist item under checklist \`${event.data.checklist.name}\` marked complete by **[${conf.realNames ? event.memberCreator.fullName : event.memberCreator.username}](https://trello.com/${event.memberCreator.username})**`)
 | 
				
			||||||
 | 
					            .addField(`Checklist Item Name`, event.data.checkItem.name.length > 1024 ? `${event.data.checkItem.name.trim().slice(0, 1020)}...` : event.data.checkItem.name)
 | 
				
			||||||
 | 
					        send(addDiscordUserData(embed, event.memberCreator))
 | 
				
			||||||
 | 
					    } else if (event.data.checkItem.state === "incomplete") {
 | 
				
			||||||
 | 
					        if (!eventEnabled(`checklistItemMarkedIncomplete`)) return
 | 
				
			||||||
 | 
					        let embed = getEmbedBase(event)
 | 
				
			||||||
 | 
					            .setTitle(`Checklist item marked incomplete!`)
 | 
				
			||||||
 | 
					            .setDescription(`**CARD:** ${event.data.card.name} — **[CARD LINK](https://trello.com/c/${event.data.card.shortLink})**\n\n**EVENT:** Checklist item under checklist \`${event.data.checklist.name}\` marked incomplete by **[${conf.realNames ? event.memberCreator.fullName : event.memberCreator.username}](https://trello.com/${event.memberCreator.username})**`)
 | 
				
			||||||
 | 
					            .addField(`Checklist Item Name`, event.data.checkItem.name.length > 1024 ? `${event.data.checkItem.name.trim().slice(0, 1020)}...` : event.data.checkItem.name)
 | 
				
			||||||
 | 
					        send(addDiscordUserData(embed, event.memberCreator))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					** =======================
 | 
				
			||||||
 | 
					** Miscellaneous functions
 | 
				
			||||||
 | 
					** =======================
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					events.on('maxId', (id) => {
 | 
				
			||||||
 | 
					    if (latestActivityID == id) return
 | 
				
			||||||
 | 
					    latestActivityID = id
 | 
				
			||||||
 | 
					    fs.writeFileSync('.latestActivityID', id)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const send = (embed, content = ``) => conf.channel.send(`${content} ${conf.contentString}`, {embed:embed}).catch(err => console.error(err))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const eventEnabled = (type) => conf.enabledEvents.length > 0 ? conf.enabledEvents.includes(type) : true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const logEventFire = (event) => console.log(`${new Date(event.date).toUTCString()} - ${event.type} fired`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const getEmbedBase = (event) => new Discord.RichEmbed()
 | 
				
			||||||
 | 
					        .setFooter(`${conf.guild.members.get(bot.user.id).displayName} • ${event.data.board.name} [${event.data.board.shortLink}]`, bot.user.displayAvatarURL)
 | 
				
			||||||
 | 
					        .setTimestamp(event.hasOwnProperty(`date`) ? event.date : Date.now())
 | 
				
			||||||
 | 
					        .setColor("#127ABD")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//  Converts Trello @username mentions in titles to Discord mentions, finds channel and role mentions, and mirros Discord user mentions outside the embed
 | 
				
			||||||
 | 
					const convertMentions = (embed, event) => {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					// adds thumbanil and appends user mention to the end of the description, if possible
 | 
				
			||||||
 | 
					const addDiscordUserData = (embed, member) => {
 | 
				
			||||||
 | 
					    if (conf.userIDs[member.username]) {
 | 
				
			||||||
 | 
					        let discordUser = conf.guild.members.get(conf.userIDs[member.username])
 | 
				
			||||||
 | 
					        if (discordUser) embed
 | 
				
			||||||
 | 
					            .setThumbnail(discordUser.user.displayAvatarURL)
 | 
				
			||||||
 | 
					            .setDescription(`${embed.description} / ${discordUser.toString()}`)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return embed
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// logs initialization data (stuff loaded from conf.json) - mostly for debugging purposes
 | 
				
			||||||
 | 
					const logInitializationData = () => console.log(`== INITIALIZING WITH:
 | 
				
			||||||
 | 
					    latestActivityID - ${latestActivityID}
 | 
				
			||||||
 | 
					    boardIDs --------- ${conf.boardIDs.length + " [" + conf.boardIDs.join(", ") + "]"}
 | 
				
			||||||
 | 
					    serverID --------- ${conf.serverID} (${conf.guild.name})
 | 
				
			||||||
 | 
					    channelID -------- ${conf.channelID} (#${conf.channel.name})
 | 
				
			||||||
 | 
					    pollInterval ----- ${conf.pollInterval} ms (${conf.pollInterval / 1000} seconds)
 | 
				
			||||||
 | 
					    prefix ----------- "${conf.prefix}"${conf.prefix === "." ? " (default)" : ""}
 | 
				
			||||||
 | 
					    contentString ---- ${conf.contentString !== "" ? "\"" + conf.contentString + "\"" : "none"}
 | 
				
			||||||
 | 
					    enabledEvents ---- ${conf.enabledEvents.length > 0 ? conf.enabledEvents.length + " [" + conf.enabledEvents.join(", ") + "]" : "all"}
 | 
				
			||||||
 | 
					    userIDs ---------- ${Object.getOwnPropertyNames(conf.userIDs).length}`)
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue