interactive bots: Add Trello Interactive Bot.
This commit is contained in:
parent
5b0a444ab8
commit
b7b083f094
|
@ -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.")
|
||||||
|
|
0
zulip_bots/zulip_bots/bots/trello/__init__.py
Normal file
0
zulip_bots/zulip_bots/bots/trello/__init__.py
Normal file
BIN
zulip_bots/zulip_bots/bots/trello/assets/list_commands.png
Normal file
BIN
zulip_bots/zulip_bots/bots/trello/assets/list_commands.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 624 KiB |
41
zulip_bots/zulip_bots/bots/trello/doc.md
Normal file
41
zulip_bots/zulip_bots/bots/trello/doc.md
Normal 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)
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
106
zulip_bots/zulip_bots/bots/trello/fixtures/get_all_boards.json
Normal file
106
zulip_bots/zulip_bots/bots/trello/fixtures/get_all_boards.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
92
zulip_bots/zulip_bots/bots/trello/fixtures/get_cards.json
Normal file
92
zulip_bots/zulip_bots/bots/trello/fixtures/get_cards.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
67
zulip_bots/zulip_bots/bots/trello/fixtures/get_lists.json
Normal file
67
zulip_bots/zulip_bots/bots/trello/fixtures/get_lists.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
1
zulip_bots/zulip_bots/bots/trello/requirements.txt
Normal file
1
zulip_bots/zulip_bots/bots/trello/requirements.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
requests
|
109
zulip_bots/zulip_bots/bots/trello/test_trello.py
Normal file
109
zulip_bots/zulip_bots/bots/trello/test_trello.py
Normal 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)
|
4
zulip_bots/zulip_bots/bots/trello/trello.conf
Normal file
4
zulip_bots/zulip_bots/bots/trello/trello.conf
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[trello]
|
||||||
|
api_key = <api_key>
|
||||||
|
access_token = <access_token>
|
||||||
|
user_name = <user_name>
|
172
zulip_bots/zulip_bots/bots/trello/trello.py
Normal file
172
zulip_bots/zulip_bots/bots/trello/trello.py
Normal 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
|
Loading…
Reference in a new issue