interactive bots: Add Trello Interactive Bot.

This commit is contained in:
Viraat Chandra 2018-01-23 22:59:26 +05:30 committed by showell
parent 5b0a444ab8
commit b7b083f094
17 changed files with 964 additions and 0 deletions

View file

@ -89,6 +89,8 @@ force_include = [
"zulip_bots/zulip_bots/bots/tictactoe/test_tictactoe.py", "zulip_bots/zulip_bots/bots/tictactoe/test_tictactoe.py",
"zulip_bots/zulip_bots/bots/game_handler_bot/game_handler_bot.py", "zulip_bots/zulip_bots/bots/game_handler_bot/game_handler_bot.py",
"zulip_bots/zulip_bots/bots/game_handler_bot/test_game_handler_bot.py", "zulip_bots/zulip_bots/bots/game_handler_bot/test_game_handler_bot.py",
"zulip_bots/zulip_bots/bots/trello/trello.py",
"zulip_bots/zulip_bots/bots/trello/test_trello.py"
] ]
parser = argparse.ArgumentParser(description="Run mypy on files tracked by git.") parser = argparse.ArgumentParser(description="Run mypy on files tracked by git.")

Binary file not shown.

After

Width:  |  Height:  |  Size: 624 KiB

View file

@ -0,0 +1,41 @@
# Trello bot
The Trello bot is a Zulip bot that enables interaction with Trello using the
[Trello API](https://developers.trello.com).
To use the Trello bot, you can simply call it with `@<botname>` followed
by a command, like so:
```
@Trello help
```
## Setup
Before usage, you will need to configure the bot by putting the value of the `<api_key>`,
`<access_token>`, and `<user_name>` in the config file.
To do this, follow the given steps:
1. Go to [this]( https://trello.com/app-key) link after logging in at
[Trello]( https://trello.com/).
2. Generate an `access_token` and note it down. Continue to get your
`api_key`.
3. Go to your profile page in Trello and note down your `username`.
4. Open up `zulip_bots/bots/trello/trello.conf` in an editor and
change the values of the `<api_key>`, `<access_token>`, and `<user_name>`
attributes to the corresponding noted values.
## Developer Notes
Be sure to add the additional commands and their descriptions to the `supported_commands`
list in `trello.py` so that they can be displayed with the other available commands using
`@<botname> list-commands`. Also modify the `test_list_commands_command` in
`test_trello.py`.
## Usage
`@Trello list-commands` - This command gives a list of all available commands along with
short descriptions.
Example:
![](assets/list_commands.png)

View file

@ -0,0 +1,48 @@
{
"request": {
"api_url": "https://api.trello.com/1/members/TEST/",
"params": {
"key": "TEST",
"token": "TEST"
}
},
"response": {
"error": "invalid id"
},
"response-headers": {
"X-Trello-Environment": "Production",
"Content-Length": "595",
"X-Trello-Version": "68",
"X-RATE-LIMIT-MEMBER-MAX": "200",
"Set-Cookie": "dsc=5cf993d4c95bff2ee0273937e979ace5b89a6629f4ec355860d1aac0e4b565cd; Path=/; Expires=Fri, 26 Jan 2018 11:29:12 GMT; Secure",
"X-Server-Time": "1516706952891",
"Surrogate-Control": "no-store",
"Expires": "Thu, 01 Jan 1970 00:00:00",
"X-RATE-LIMIT-MEMBER-REMAINING": "199",
"X-RATE-LIMIT-API-TOKEN-MAX": "100",
"X-Content-Type-Options": "nosniff",
"X-RATE-LIMIT-API-KEY-MAX": "300",
"Content-Encoding": "gzip",
"X-RATE-LIMIT-API-TOKEN-INTERVAL-MS": "10000",
"ETag": "W/\"FTxOEKThSaPt1Pa7iy1iSg==\"",
"Access-Control-Allow-Methods": "GET, PUT, POST, DELETE",
"X-RATE-LIMIT-API-KEY-INTERVAL-MS": "10000",
"Date": "Tue, 23 Jan 2018 11:29:13 GMT",
"X-Frame-Options": "DENY",
"Access-Control-Allow-Headers": "Authorization, Accept, Content-Type",
"Pragma": "no-cache",
"X-XSS-Protection": "1; mode=block",
"Referrer-Policy": "strict-origin-when-cross-origin",
"X-Download-Options": "noopen",
"X-RATE-LIMIT-API-KEY-REMAINING": "299",
"Cache-Control": "max-age=0, must-revalidate, no-cache, no-store",
"X-DNS-Prefetch-Control": "off",
"Strict-Transport-Security": "max-age=15552000",
"Content-Type": "application/json; charset=utf-8",
"X-RATE-LIMIT-MEMBER-INTERVAL-MS": "10000",
"X-RATE-LIMIT-API-TOKEN-REMAINING": "99",
"Access-Control-Allow-Origin": "*",
"Connection": "keep-alive",
"Vary": "Accept-Encoding"
}
}

View file

@ -0,0 +1,48 @@
{
"request": {
"api_url": "https://api.trello.com/1/boards/TEST/cards",
"params": {
"key": "TEST",
"token": "TEST"
}
},
"response": {
"error": "invalid id"
},
"response-headers": {
"X-Trello-Environment": "Production",
"Content-Length": "595",
"X-Trello-Version": "68",
"X-RATE-LIMIT-MEMBER-MAX": "200",
"Set-Cookie": "dsc=5cf993d4c95bff2ee0273937e979ace5b89a6629f4ec355860d1aac0e4b565cd; Path=/; Expires=Fri, 26 Jan 2018 11:29:12 GMT; Secure",
"X-Server-Time": "1516706952891",
"Surrogate-Control": "no-store",
"Expires": "Thu, 01 Jan 1970 00:00:00",
"X-RATE-LIMIT-MEMBER-REMAINING": "199",
"X-RATE-LIMIT-API-TOKEN-MAX": "100",
"X-Content-Type-Options": "nosniff",
"X-RATE-LIMIT-API-KEY-MAX": "300",
"Content-Encoding": "gzip",
"X-RATE-LIMIT-API-TOKEN-INTERVAL-MS": "10000",
"ETag": "W/\"FTxOEKThSaPt1Pa7iy1iSg==\"",
"Access-Control-Allow-Methods": "GET, PUT, POST, DELETE",
"X-RATE-LIMIT-API-KEY-INTERVAL-MS": "10000",
"Date": "Tue, 23 Jan 2018 11:29:13 GMT",
"X-Frame-Options": "DENY",
"Access-Control-Allow-Headers": "Authorization, Accept, Content-Type",
"Pragma": "no-cache",
"X-XSS-Protection": "1; mode=block",
"Referrer-Policy": "strict-origin-when-cross-origin",
"X-Download-Options": "noopen",
"X-RATE-LIMIT-API-KEY-REMAINING": "299",
"Cache-Control": "max-age=0, must-revalidate, no-cache, no-store",
"X-DNS-Prefetch-Control": "off",
"Strict-Transport-Security": "max-age=15552000",
"Content-Type": "application/json; charset=utf-8",
"X-RATE-LIMIT-MEMBER-INTERVAL-MS": "10000",
"X-RATE-LIMIT-API-TOKEN-REMAINING": "99",
"Access-Control-Allow-Origin": "*",
"Connection": "keep-alive",
"Vary": "Accept-Encoding"
}
}

View file

@ -0,0 +1,48 @@
{
"request": {
"api_url": "https://api.trello.com/1/cards/TEST/checklists/",
"params": {
"key": "TEST",
"token": "TEST"
}
},
"response": {
"error": "invalid id"
},
"response-headers": {
"X-Trello-Environment": "Production",
"Content-Length": "595",
"X-Trello-Version": "68",
"X-RATE-LIMIT-MEMBER-MAX": "200",
"Set-Cookie": "dsc=5cf993d4c95bff2ee0273937e979ace5b89a6629f4ec355860d1aac0e4b565cd; Path=/; Expires=Fri, 26 Jan 2018 11:29:12 GMT; Secure",
"X-Server-Time": "1516706952891",
"Surrogate-Control": "no-store",
"Expires": "Thu, 01 Jan 1970 00:00:00",
"X-RATE-LIMIT-MEMBER-REMAINING": "199",
"X-RATE-LIMIT-API-TOKEN-MAX": "100",
"X-Content-Type-Options": "nosniff",
"X-RATE-LIMIT-API-KEY-MAX": "300",
"Content-Encoding": "gzip",
"X-RATE-LIMIT-API-TOKEN-INTERVAL-MS": "10000",
"ETag": "W/\"FTxOEKThSaPt1Pa7iy1iSg==\"",
"Access-Control-Allow-Methods": "GET, PUT, POST, DELETE",
"X-RATE-LIMIT-API-KEY-INTERVAL-MS": "10000",
"Date": "Tue, 23 Jan 2018 11:29:13 GMT",
"X-Frame-Options": "DENY",
"Access-Control-Allow-Headers": "Authorization, Accept, Content-Type",
"Pragma": "no-cache",
"X-XSS-Protection": "1; mode=block",
"Referrer-Policy": "strict-origin-when-cross-origin",
"X-Download-Options": "noopen",
"X-RATE-LIMIT-API-KEY-REMAINING": "299",
"Cache-Control": "max-age=0, must-revalidate, no-cache, no-store",
"X-DNS-Prefetch-Control": "off",
"Strict-Transport-Security": "max-age=15552000",
"Content-Type": "application/json; charset=utf-8",
"X-RATE-LIMIT-MEMBER-INTERVAL-MS": "10000",
"X-RATE-LIMIT-API-TOKEN-REMAINING": "99",
"Access-Control-Allow-Origin": "*",
"Connection": "keep-alive",
"Vary": "Accept-Encoding"
}
}

View file

@ -0,0 +1,48 @@
{
"request": {
"api_url": "https://api.trello.com/1/boards/TEST/lists",
"params": {
"key": "TEST",
"token": "TEST"
}
},
"response": {
"error": "invalid id"
},
"response-headers": {
"X-Trello-Environment": "Production",
"Content-Length": "595",
"X-Trello-Version": "68",
"X-RATE-LIMIT-MEMBER-MAX": "200",
"Set-Cookie": "dsc=5cf993d4c95bff2ee0273937e979ace5b89a6629f4ec355860d1aac0e4b565cd; Path=/; Expires=Fri, 26 Jan 2018 11:29:12 GMT; Secure",
"X-Server-Time": "1516706952891",
"Surrogate-Control": "no-store",
"Expires": "Thu, 01 Jan 1970 00:00:00",
"X-RATE-LIMIT-MEMBER-REMAINING": "199",
"X-RATE-LIMIT-API-TOKEN-MAX": "100",
"X-Content-Type-Options": "nosniff",
"X-RATE-LIMIT-API-KEY-MAX": "300",
"Content-Encoding": "gzip",
"X-RATE-LIMIT-API-TOKEN-INTERVAL-MS": "10000",
"ETag": "W/\"FTxOEKThSaPt1Pa7iy1iSg==\"",
"Access-Control-Allow-Methods": "GET, PUT, POST, DELETE",
"X-RATE-LIMIT-API-KEY-INTERVAL-MS": "10000",
"Date": "Tue, 23 Jan 2018 11:29:13 GMT",
"X-Frame-Options": "DENY",
"Access-Control-Allow-Headers": "Authorization, Accept, Content-Type",
"Pragma": "no-cache",
"X-XSS-Protection": "1; mode=block",
"Referrer-Policy": "strict-origin-when-cross-origin",
"X-Download-Options": "noopen",
"X-RATE-LIMIT-API-KEY-REMAINING": "299",
"Cache-Control": "max-age=0, must-revalidate, no-cache, no-store",
"X-DNS-Prefetch-Control": "off",
"Strict-Transport-Security": "max-age=15552000",
"Content-Type": "application/json; charset=utf-8",
"X-RATE-LIMIT-MEMBER-INTERVAL-MS": "10000",
"X-RATE-LIMIT-API-TOKEN-REMAINING": "99",
"Access-Control-Allow-Origin": "*",
"Connection": "keep-alive",
"Vary": "Accept-Encoding"
}
}

View file

@ -0,0 +1,106 @@
{
"request": {
"api_url": "https://api.trello.com/1/members/TEST/",
"params": {
"key": "TEST",
"token": "TEST"
}
},
"response": {
"id": "TEST",
"avatarHash": null,
"bio": "",
"bioData": null,
"confirmed": true,
"fullName": "TEST",
"idEnterprisesDeactivated": [],
"idPremOrgsAdmin": [],
"initials": "TEST",
"memberType": "normal",
"products": [],
"status": "disconnected",
"url": "https://trello.com/TEST",
"username": "TEST",
"avatarSource": "none",
"email": "TEST",
"gravatarHash": "TEST",
"idBoards": [
],
"idEnterprise": null,
"idOrganizations": [],
"idEnterprisesAdmin": [],
"limits": {
"boards": {
"totalPerMember": {
"status": "ok",
"disableAt": 950,
"warnAt": 900
}
},
"orgs": {
"totalPerMember": {
"status": "ok",
"disableAt": 95,
"warnAt": 90
}
}
},
"loginTypes": [
"android",
"password"
],
"oneTimeMessagesDismissed": [
"PowerUpsLimitFullAd",
"GhostListDismissed-2",
"GhostListDismissed-3"
],
"messagesDismissed": [],
"prefs": {
"sendSummaries": true,
"minutesBetweenSummaries": 15,
"minutesBeforeDeadlineToNotify": 1440,
"colorBlind": false,
"locale": "en-GB"
},
"trophies": [],
"uploadedAvatarHash": null,
"premiumFeatures": [],
"idBoardsPinned": null
},
"response-headers": {
"X-Trello-Environment": "Production",
"Content-Length": "595",
"X-Trello-Version": "68",
"X-RATE-LIMIT-MEMBER-MAX": "200",
"Set-Cookie": "dsc=5cf993d4c95bff2ee0273937e979ace5b89a6629f4ec355860d1aac0e4b565cd; Path=/; Expires=Fri, 26 Jan 2018 11:29:12 GMT; Secure",
"X-Server-Time": "1516706952891",
"Surrogate-Control": "no-store",
"Expires": "Thu, 01 Jan 1970 00:00:00",
"X-RATE-LIMIT-MEMBER-REMAINING": "199",
"X-RATE-LIMIT-API-TOKEN-MAX": "100",
"X-Content-Type-Options": "nosniff",
"X-RATE-LIMIT-API-KEY-MAX": "300",
"Content-Encoding": "gzip",
"X-RATE-LIMIT-API-TOKEN-INTERVAL-MS": "10000",
"ETag": "W/\"FTxOEKThSaPt1Pa7iy1iSg==\"",
"Access-Control-Allow-Methods": "GET, PUT, POST, DELETE",
"X-RATE-LIMIT-API-KEY-INTERVAL-MS": "10000",
"Date": "Tue, 23 Jan 2018 11:29:13 GMT",
"X-Frame-Options": "DENY",
"Access-Control-Allow-Headers": "Authorization, Accept, Content-Type",
"Pragma": "no-cache",
"X-XSS-Protection": "1; mode=block",
"Referrer-Policy": "strict-origin-when-cross-origin",
"X-Download-Options": "noopen",
"X-RATE-LIMIT-API-KEY-REMAINING": "299",
"Cache-Control": "max-age=0, must-revalidate, no-cache, no-store",
"X-DNS-Prefetch-Control": "off",
"Strict-Transport-Security": "max-age=15552000",
"Content-Type": "application/json; charset=utf-8",
"X-RATE-LIMIT-MEMBER-INTERVAL-MS": "10000",
"X-RATE-LIMIT-API-TOKEN-REMAINING": "99",
"Access-Control-Allow-Origin": "*",
"Connection": "keep-alive",
"Vary": "Accept-Encoding"
}
}

View file

@ -0,0 +1,90 @@
{
"request": {
"api_url": "https://api.trello.com/1/boards/TEST/",
"params": {
"key": "TEST",
"token": "TEST"
}
},
"response": {
"id": "TEST",
"name": "TEST",
"desc": "",
"descData": null,
"closed": false,
"idOrganization": null,
"pinned": false,
"url": "TEST",
"shortUrl": "TEST",
"prefs": {
"permissionLevel": "private",
"voting": "disabled",
"comments": "members",
"invitations": "members",
"selfJoin": false,
"cardCovers": true,
"cardAging": "regular",
"calendarFeedEnabled": false,
"background": "green",
"backgroundImage": null,
"backgroundImageScaled": null,
"backgroundTile": false,
"backgroundBrightness": "dark",
"backgroundColor": "#519839",
"backgroundBottomColor": "#519839",
"backgroundTopColor": "#519839",
"canBePublic": true,
"canBeOrg": true,
"canBePrivate": true,
"canInvite": true
},
"labelNames": {
"green": "",
"yellow": "",
"orange": "",
"red": "",
"purple": "",
"blue": "",
"sky": "",
"lime": "",
"pink": "",
"black": ""
}
},
"response-headers": {
"X-Trello-Environment": "Production",
"Content-Length": "595",
"X-Trello-Version": "68",
"X-RATE-LIMIT-MEMBER-MAX": "200",
"Set-Cookie": "dsc=5cf993d4c95bff2ee0273937e979ace5b89a6629f4ec355860d1aac0e4b565cd; Path=/; Expires=Fri, 26 Jan 2018 11:29:12 GMT; Secure",
"X-Server-Time": "1516706952891",
"Surrogate-Control": "no-store",
"Expires": "Thu, 01 Jan 1970 00:00:00",
"X-RATE-LIMIT-MEMBER-REMAINING": "199",
"X-RATE-LIMIT-API-TOKEN-MAX": "100",
"X-Content-Type-Options": "nosniff",
"X-RATE-LIMIT-API-KEY-MAX": "300",
"Content-Encoding": "gzip",
"X-RATE-LIMIT-API-TOKEN-INTERVAL-MS": "10000",
"ETag": "W/\"FTxOEKThSaPt1Pa7iy1iSg==\"",
"Access-Control-Allow-Methods": "GET, PUT, POST, DELETE",
"X-RATE-LIMIT-API-KEY-INTERVAL-MS": "10000",
"Date": "Tue, 23 Jan 2018 11:29:13 GMT",
"X-Frame-Options": "DENY",
"Access-Control-Allow-Headers": "Authorization, Accept, Content-Type",
"Pragma": "no-cache",
"X-XSS-Protection": "1; mode=block",
"Referrer-Policy": "strict-origin-when-cross-origin",
"X-Download-Options": "noopen",
"X-RATE-LIMIT-API-KEY-REMAINING": "299",
"Cache-Control": "max-age=0, must-revalidate, no-cache, no-store",
"X-DNS-Prefetch-Control": "off",
"Strict-Transport-Security": "max-age=15552000",
"Content-Type": "application/json; charset=utf-8",
"X-RATE-LIMIT-MEMBER-INTERVAL-MS": "10000",
"X-RATE-LIMIT-API-TOKEN-REMAINING": "99",
"Access-Control-Allow-Origin": "*",
"Connection": "keep-alive",
"Vary": "Accept-Encoding"
}
}

View file

@ -0,0 +1,92 @@
{
"request": {
"api_url": "https://api.trello.com/1/boards/TEST/cards",
"params": {
"key": "TEST",
"token": "TEST"
}
},
"response": [
{
"id": "TEST",
"checkItemStates": null,
"closed": false,
"dateLastActivity": "2018-01-03T08:27:54.579Z",
"desc": "",
"descData": null,
"idBoard": "TEST",
"idList": "TEST",
"idMembersVoted": [],
"idShort": 12,
"idAttachmentCover": null,
"idLabels": [],
"manualCoverAttachment": false,
"name": "TEST",
"pos": 65535,
"shortLink": "TEST",
"badges": {
"votes": 0,
"attachmentsByType": {
"trello": {
"board": 0,
"card": 0
}
},
"viewingMemberVoted": false,
"subscribed": false,
"fogbugz": "",
"checkItems": 0,
"checkItemsChecked": 0,
"comments": 0,
"attachments": 0,
"description": false,
"due": null,
"dueComplete": false
},
"dueComplete": false,
"due": null,
"idChecklists": [],
"idMembers": [],
"labels": [],
"shortUrl": "TEST",
"subscribed": false,
"url": "TEST"
}
],
"response-headers": {
"X-Trello-Environment": "Production",
"Content-Length": "595",
"X-Trello-Version": "68",
"X-RATE-LIMIT-MEMBER-MAX": "200",
"Set-Cookie": "dsc=5cf993d4c95bff2ee0273937e979ace5b89a6629f4ec355860d1aac0e4b565cd; Path=/; Expires=Fri, 26 Jan 2018 11:29:12 GMT; Secure",
"X-Server-Time": "1516706952891",
"Surrogate-Control": "no-store",
"Expires": "Thu, 01 Jan 1970 00:00:00",
"X-RATE-LIMIT-MEMBER-REMAINING": "199",
"X-RATE-LIMIT-API-TOKEN-MAX": "100",
"X-Content-Type-Options": "nosniff",
"X-RATE-LIMIT-API-KEY-MAX": "300",
"Content-Encoding": "gzip",
"X-RATE-LIMIT-API-TOKEN-INTERVAL-MS": "10000",
"ETag": "W/\"FTxOEKThSaPt1Pa7iy1iSg==\"",
"Access-Control-Allow-Methods": "GET, PUT, POST, DELETE",
"X-RATE-LIMIT-API-KEY-INTERVAL-MS": "10000",
"Date": "Tue, 23 Jan 2018 11:29:13 GMT",
"X-Frame-Options": "DENY",
"Access-Control-Allow-Headers": "Authorization, Accept, Content-Type",
"Pragma": "no-cache",
"X-XSS-Protection": "1; mode=block",
"Referrer-Policy": "strict-origin-when-cross-origin",
"X-Download-Options": "noopen",
"X-RATE-LIMIT-API-KEY-REMAINING": "299",
"Cache-Control": "max-age=0, must-revalidate, no-cache, no-store",
"X-DNS-Prefetch-Control": "off",
"Strict-Transport-Security": "max-age=15552000",
"Content-Type": "application/json; charset=utf-8",
"X-RATE-LIMIT-MEMBER-INTERVAL-MS": "10000",
"X-RATE-LIMIT-API-TOKEN-REMAINING": "99",
"Access-Control-Allow-Origin": "*",
"Connection": "keep-alive",
"Vary": "Accept-Encoding"
}
}

View file

@ -0,0 +1,88 @@
{
"request": {
"api_url": "https://api.trello.com/1/cards/TEST/checklists/",
"params": {
"key": "TEST",
"token": "TEST"
}
},
"response": [
{
"id": "TEST",
"name": "TEST",
"idBoard": "TEST",
"idCard": "TEST",
"pos": 16384,
"checkItems": [
{
"state": "complete",
"idChecklist": "TEST",
"id": "TEST",
"name": "TEST_1",
"nameData": null,
"pos": 17350
},
{
"state": "complete",
"idChecklist": "TEST",
"id": "TEST",
"name": "TEST_2",
"nameData": null,
"pos": 34343
},
{
"state": "incomplete",
"idChecklist": "TEST",
"id": "TEST",
"name": "TEST_3",
"nameData": null,
"pos": 51594
},
{
"state": "incomplete",
"idChecklist": "TEST",
"id": "TEST",
"name": "TEST_4",
"nameData": null,
"pos": 67998
}
]
}
],
"response-headers": {
"X-Trello-Environment": "Production",
"Content-Length": "595",
"X-Trello-Version": "68",
"X-RATE-LIMIT-MEMBER-MAX": "200",
"Set-Cookie": "dsc=5cf993d4c95bff2ee0273937e979ace5b89a6629f4ec355860d1aac0e4b565cd; Path=/; Expires=Fri, 26 Jan 2018 11:29:12 GMT; Secure",
"X-Server-Time": "1516706952891",
"Surrogate-Control": "no-store",
"Expires": "Thu, 01 Jan 1970 00:00:00",
"X-RATE-LIMIT-MEMBER-REMAINING": "199",
"X-RATE-LIMIT-API-TOKEN-MAX": "100",
"X-Content-Type-Options": "nosniff",
"X-RATE-LIMIT-API-KEY-MAX": "300",
"Content-Encoding": "gzip",
"X-RATE-LIMIT-API-TOKEN-INTERVAL-MS": "10000",
"ETag": "W/\"FTxOEKThSaPt1Pa7iy1iSg==\"",
"Access-Control-Allow-Methods": "GET, PUT, POST, DELETE",
"X-RATE-LIMIT-API-KEY-INTERVAL-MS": "10000",
"Date": "Tue, 23 Jan 2018 11:29:13 GMT",
"X-Frame-Options": "DENY",
"Access-Control-Allow-Headers": "Authorization, Accept, Content-Type",
"Pragma": "no-cache",
"X-XSS-Protection": "1; mode=block",
"Referrer-Policy": "strict-origin-when-cross-origin",
"X-Download-Options": "noopen",
"X-RATE-LIMIT-API-KEY-REMAINING": "299",
"Cache-Control": "max-age=0, must-revalidate, no-cache, no-store",
"X-DNS-Prefetch-Control": "off",
"Strict-Transport-Security": "max-age=15552000",
"Content-Type": "application/json; charset=utf-8",
"X-RATE-LIMIT-MEMBER-INTERVAL-MS": "10000",
"X-RATE-LIMIT-API-TOKEN-REMAINING": "99",
"Access-Control-Allow-Origin": "*",
"Connection": "keep-alive",
"Vary": "Accept-Encoding"
}
}

View file

@ -0,0 +1,67 @@
{
"request": {
"api_url": "https://api.trello.com/1/boards/TEST/lists",
"params": {
"key": "TEST",
"token": "TEST"
}
},
"response": [
{
"id": "TEST",
"name": "TEST_A",
"cards": [
{
"id": "TEST",
"name": "TEST_1"
}
]
},
{
"id": "TEST",
"name": "TEST_B",
"cards": [
{
"id": "TEST",
"name": "TEST_2"
}
]
}
],
"response-headers": {
"X-Trello-Environment": "Production",
"Content-Length": "595",
"X-Trello-Version": "68",
"X-RATE-LIMIT-MEMBER-MAX": "200",
"Set-Cookie": "dsc=5cf993d4c95bff2ee0273937e979ace5b89a6629f4ec355860d1aac0e4b565cd; Path=/; Expires=Fri, 26 Jan 2018 11:29:12 GMT; Secure",
"X-Server-Time": "1516706952891",
"Surrogate-Control": "no-store",
"Expires": "Thu, 01 Jan 1970 00:00:00",
"X-RATE-LIMIT-MEMBER-REMAINING": "199",
"X-RATE-LIMIT-API-TOKEN-MAX": "100",
"X-Content-Type-Options": "nosniff",
"X-RATE-LIMIT-API-KEY-MAX": "300",
"Content-Encoding": "gzip",
"X-RATE-LIMIT-API-TOKEN-INTERVAL-MS": "10000",
"ETag": "W/\"FTxOEKThSaPt1Pa7iy1iSg==\"",
"Access-Control-Allow-Methods": "GET, PUT, POST, DELETE",
"X-RATE-LIMIT-API-KEY-INTERVAL-MS": "10000",
"Date": "Tue, 23 Jan 2018 11:29:13 GMT",
"X-Frame-Options": "DENY",
"Access-Control-Allow-Headers": "Authorization, Accept, Content-Type",
"Pragma": "no-cache",
"X-XSS-Protection": "1; mode=block",
"Referrer-Policy": "strict-origin-when-cross-origin",
"X-Download-Options": "noopen",
"X-RATE-LIMIT-API-KEY-REMAINING": "299",
"Cache-Control": "max-age=0, must-revalidate, no-cache, no-store",
"X-DNS-Prefetch-Control": "off",
"Strict-Transport-Security": "max-age=15552000",
"Content-Type": "application/json; charset=utf-8",
"X-RATE-LIMIT-MEMBER-INTERVAL-MS": "10000",
"X-RATE-LIMIT-API-TOKEN-REMAINING": "99",
"Access-Control-Allow-Origin": "*",
"Connection": "keep-alive",
"Vary": "Accept-Encoding"
}
}

View file

@ -0,0 +1 @@
requests

View file

@ -0,0 +1,109 @@
from unittest.mock import patch
from zulip_bots.bots.trello.trello import TrelloHandler
from zulip_bots.test_lib import BotTestCase
from zulip_bots.test_lib import StubBotHandler
mock_config = {
'api_key': 'TEST',
'access_token': 'TEST',
'user_name': 'TEST'
}
class TestTrelloBot(BotTestCase):
bot_name = "trello" # type: str
def test_bot_responds_to_empty_message(self) -> None:
with self.mock_config_info(mock_config), patch('requests.get'):
self.verify_reply('', 'Empty Query')
def test_bot_usage(self) -> None:
with self.mock_config_info(mock_config), patch('requests.get'):
self.verify_reply('help', '''
This interactive bot can be used to interact with Trello.
Use `list-commands` to get information about the supported commands.
''')
def test_bot_quit_with_invalid_config(self) -> None:
with self.mock_config_info(mock_config), self.assertRaises(StubBotHandler.BotQuitException):
TrelloHandler().initialize(StubBotHandler())
def test_invalid_command(self) -> None:
with self.mock_config_info(mock_config), patch('requests.get'):
self.verify_reply('abcd', 'Command not supported')
def test_list_commands_command(self) -> None:
expected_reply = ('**Commands:** \n'
'1. **help**: Get the bot usage information.\n'
'2. **list-commands**: Get information about the commands supported by the bot.\n'
'3. **get-all-boards**: Get all the boards under the configured account.\n'
'4. **get-all-cards <board_id>**: Get all the cards in the given board.\n'
'5. **get-all-checklists <card_id>**: Get all the checklists in the given card.\n'
'6. **get-all-lists <board_id>**: Get all the lists in the given board.\n')
with self.mock_config_info(mock_config), patch('requests.get'):
self.verify_reply('list-commands', expected_reply)
def test_get_all_boards_command(self) -> None:
with self.mock_config_info(mock_config), patch('requests.get'):
with self.mock_http_conversation('get_all_boards'):
self.verify_reply('get-all-boards', '**Boards:** \n')
with self.mock_http_conversation('get_board_descs'):
bot_instance = TrelloHandler()
bot_instance.initialize(StubBotHandler)
self.assertEqual(bot_instance.get_board_descs(['TEST']), '1.[TEST](TEST) (`TEST`)\n')
def test_get_all_cards_command(self) -> None:
with self.mock_config_info(mock_config), patch('requests.get'):
with self.mock_http_conversation('get_cards'):
self.verify_reply('get-all-cards TEST', '**Cards:** \n1. [TEST](TEST) (`TEST`)\n')
def test_get_all_checklists_command(self) -> None:
with self.mock_config_info(mock_config), patch('requests.get'):
with self.mock_http_conversation('get_checklists'):
self.verify_reply('get-all-checklists TEST', '**Checklists:** \n'
'1. `TEST`:\n'
' * [X] TEST_1\n * [X] TEST_2\n'
' * [-] TEST_3\n * [-] TEST_4\n')
def test_get_all_lists_command(self) -> None:
with self.mock_config_info(mock_config), patch('requests.get'):
with self.mock_http_conversation('get_lists'):
self.verify_reply('get-all-lists TEST', ('**Lists:** \n'
'1. TEST_A\n'
' * TEST_1\n'
'2. TEST_B\n'
' * TEST_2\n'))
def test_command_exceptions(self) -> None:
"""Add appropriate tests here for all additional commands with try/except blocks.
This ensures consistency."""
expected_error_response = 'Invalid Response. Please check configuration and parameters.'
with self.mock_config_info(mock_config), patch('requests.get'):
with self.mock_http_conversation('exception_boards'):
self.verify_reply('get-all-boards', expected_error_response)
with self.mock_http_conversation('exception_cards'):
self.verify_reply('get-all-cards TEST', expected_error_response)
with self.mock_http_conversation('exception_checklists'):
self.verify_reply('get-all-checklists TEST', expected_error_response)
with self.mock_http_conversation('exception_lists'):
self.verify_reply('get-all-lists TEST', expected_error_response)
def test_command_invalid_arguments(self) -> None:
"""Add appropriate tests here for all additional commands with more than one arguments.
This ensures consistency."""
expected_error_response = 'Invalid Arguments.'
with self.mock_config_info(mock_config), patch('requests.get'):
self.verify_reply('get-all-cards', expected_error_response)
self.verify_reply('get-all-checklists', expected_error_response)
self.verify_reply('get-all-lists', expected_error_response)

View file

@ -0,0 +1,4 @@
[trello]
api_key = <api_key>
access_token = <access_token>
user_name = <user_name>

View file

@ -0,0 +1,172 @@
from typing import Any, List
import requests
supported_commands = [
('help', 'Get the bot usage information.'),
('list-commands', 'Get information about the commands supported by the bot.'),
('get-all-boards', 'Get all the boards under the configured account.'),
('get-all-cards <board_id>', 'Get all the cards in the given board.'),
('get-all-checklists <card_id>', 'Get all the checklists in the given card.'),
('get-all-lists <board_id>', 'Get all the lists in the given board.')
]
INVALID_ARGUMENTS_ERROR_MESSAGE = 'Invalid Arguments.'
RESPONSE_ERROR_MESSAGE = 'Invalid Response. Please check configuration and parameters.'
class TrelloHandler(object):
def initialize(self, bot_handler: Any) -> None:
self.config_info = bot_handler.get_config_info('trello')
self.api_key = self.config_info['api_key']
self.access_token = self.config_info['access_token']
self.user_name = self.config_info['user_name']
self.auth_params = {
'key': self.api_key,
'token': self.access_token
}
self.check_access_token(bot_handler)
def check_access_token(self, bot_handler: Any) -> None:
test_query_response = requests.get('https://api.trello.com/1/members/{}/'.format(self.user_name),
params=self.auth_params)
if test_query_response.text == 'invalid key':
bot_handler.quit('Invalid Credentials. Please see doc.md to find out how to get them.')
def usage(self) -> str:
return '''
This interactive bot can be used to interact with Trello.
Use `list-commands` to get information about the supported commands.
'''
def handle_message(self, message: Any, bot_handler: Any) -> None:
content = message['content'].strip()
if content == '':
bot_handler.send_reply(message, 'Empty Query')
return
elif content.lower() == 'help':
bot_handler.send_reply(message, self.usage())
return
if content.lower() == 'list-commands':
bot_reply = self.get_all_supported_commands()
elif content.lower() == 'get-all-boards':
bot_reply = self.get_all_boards()
else:
content = content.split()
content[0] = content[0].lower()
if content[0] == 'get-all-cards':
bot_reply = self.get_all_cards(content)
elif content[0] == 'get-all-checklists':
bot_reply = self.get_all_checklists(content)
elif content[0] == 'get-all-lists':
bot_reply = self.get_all_lists(content)
else:
bot_reply = 'Command not supported'
bot_handler.send_reply(message, bot_reply)
def get_all_supported_commands(self) -> str:
bot_response = '**Commands:** \n'
for index, (command, desc) in enumerate(supported_commands):
bot_response += '{}. **{}**: {}\n'.format(index + 1, command, desc)
return bot_response
def get_all_boards(self) -> str:
get_board_ids_url = 'https://api.trello.com/1/members/{}/'.format(self.user_name)
board_ids_response = requests.get(get_board_ids_url, params=self.auth_params)
try:
boards = board_ids_response.json()['idBoards']
bot_response = '**Boards:** \n' + self.get_board_descs(boards)
except (KeyError, ValueError, TypeError):
return RESPONSE_ERROR_MESSAGE
return bot_response
def get_board_descs(self, boards: List[str]) -> str:
bot_response = ''
get_board_desc_url = 'https://api.trello.com/1/boards/{}/'
for index, board in enumerate(boards):
board_desc_response = requests.get(get_board_desc_url.format(board), params=self.auth_params)
board_data = board_desc_response.json()
bot_response += '{}.[{}]({}) (`{}`)\n'.format(index + 1, board_data['name'], board_data['url'],
board_data['id'])
return bot_response
def get_all_cards(self, content: List[str]) -> str:
if len(content) != 2:
return INVALID_ARGUMENTS_ERROR_MESSAGE
board_id = content[1]
get_cards_url = 'https://api.trello.com/1/boards/{}/cards'.format(board_id)
cards_response = requests.get(get_cards_url, params=self.auth_params)
try:
cards = cards_response.json()
bot_response = '**Cards:** \n'
for index, card in enumerate(cards):
bot_response += '{}. [{}]({}) (`{}`)\n'.format(index + 1, card['name'], card['url'], card['id'])
except (KeyError, ValueError, TypeError):
return RESPONSE_ERROR_MESSAGE
return bot_response
def get_all_checklists(self, content: List[str]) -> str:
if len(content) != 2:
return INVALID_ARGUMENTS_ERROR_MESSAGE
card_id = content[1]
get_checklists_url = 'https://api.trello.com/1/cards/{}/checklists/'.format(card_id)
checklists_response = requests.get(get_checklists_url, params=self.auth_params)
try:
checklists = checklists_response.json()
bot_response = '**Checklists:** \n'
for index, checklist in enumerate(checklists):
bot_response += '{}. `{}`:\n'.format(index + 1, checklist['name'])
if 'checkItems' in checklist:
for item in checklist['checkItems']:
bot_response += ' * [{}] {}\n'.format('X' if item['state'] == 'complete' else '-', item['name'])
except (KeyError, ValueError, TypeError):
return RESPONSE_ERROR_MESSAGE
return bot_response
def get_all_lists(self, content: List[str]) -> str:
if len(content) != 2:
return INVALID_ARGUMENTS_ERROR_MESSAGE
board_id = content[1]
get_lists_url = 'https://api.trello.com/1/boards/{}/lists'.format(board_id)
lists_response = requests.get(get_lists_url, params=self.auth_params)
try:
lists = lists_response.json()
bot_response = '**Lists:** \n'
for index, _list in enumerate(lists):
bot_response += '{}. {}\n'.format(index + 1, _list['name'])
if 'cards' in _list:
for card in _list['cards']:
bot_response += ' * {}\n'.format(card['name'])
except (KeyError, ValueError, TypeError):
return RESPONSE_ERROR_MESSAGE
return bot_response
handler_class = TrelloHandler