interactive bots: Create Mention bot.
This commit is contained in:
parent
6121002b35
commit
a2557ccbe6
|
@ -74,7 +74,9 @@ force_include = [
|
||||||
"zulip_bots/zulip_bots/bots/yoda/yoda.py",
|
"zulip_bots/zulip_bots/bots/yoda/yoda.py",
|
||||||
"zulip_bots/zulip_bots/bots/yoda/test_yoda.py",
|
"zulip_bots/zulip_bots/bots/yoda/test_yoda.py",
|
||||||
"zulip_bots/zulip_bots/bots/dialogflow/dialogflow.py",
|
"zulip_bots/zulip_bots/bots/dialogflow/dialogflow.py",
|
||||||
"zulip_bots/zulip_bots/bots/dialogflow/test_dialogflow.py"
|
"zulip_bots/zulip_bots/bots/dialogflow/test_dialogflow.py",
|
||||||
|
"zulip_bots/zulip_bots/bots/mention/mention.py",
|
||||||
|
"zulip_bots/zulip_bots/bots/mention/test_mention.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/mention/__init__.py
Normal file
0
zulip_bots/zulip_bots/bots/mention/__init__.py
Normal file
BIN
zulip_bots/zulip_bots/bots/mention/assets/mentions_demo.png
Normal file
BIN
zulip_bots/zulip_bots/bots/mention/assets/mentions_demo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 93 KiB |
32
zulip_bots/zulip_bots/bots/mention/doc.md
Normal file
32
zulip_bots/zulip_bots/bots/mention/doc.md
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
# Mention bot
|
||||||
|
|
||||||
|
The Mention bot is a Zulip bot that can fetch Mentions associated with
|
||||||
|
a given keyword from the web using [Mention](https://mention.com/en/).
|
||||||
|
|
||||||
|
To use the Mention bot, you can simply call it with `@<botname>` followed
|
||||||
|
by a keyword, like so:
|
||||||
|
|
||||||
|
```
|
||||||
|
@Mention Apple
|
||||||
|
```
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
Before you can proceed further, you'll need to go to the
|
||||||
|
[Mention Dev](https://dev.mention.com/login), and get a
|
||||||
|
Mention API Access Token.
|
||||||
|
|
||||||
|
1. Login.
|
||||||
|
2. Enter the **App Name**, **Description**, **Website**, and **Redirect uris**. In this version, there
|
||||||
|
is no actual use of the Redirect Uri and Website.
|
||||||
|
3. After accepting the agreement, click on **Create New App**.
|
||||||
|
4. And you're done! You should now have an Access Token.
|
||||||
|
5. Open up `zulip_bots/bots/mention/mention.conf` in an editor and
|
||||||
|
change the value of the `<access_token>` attribute to the Access Token
|
||||||
|
you generated above.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
`@Mention <keyword>` - This command will fetch the most recent 20
|
||||||
|
mentions of the keyword on the web (Limitations of a free account).
|
||||||
|
Example:
|
||||||
|
![](assets/mentions_demo.png)
|
125
zulip_bots/zulip_bots/bots/mention/fixtures/create_alert.json
Normal file
125
zulip_bots/zulip_bots/bots/mention/fixtures/create_alert.json
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
{
|
||||||
|
"alert": {
|
||||||
|
"id": "112233",
|
||||||
|
"name": "NASA and competitors",
|
||||||
|
"query": {
|
||||||
|
"type": "basic",
|
||||||
|
"included_keywords": [
|
||||||
|
"NASA",
|
||||||
|
"Arianespace",
|
||||||
|
"SpaceX",
|
||||||
|
"Pockocmoc"
|
||||||
|
],
|
||||||
|
"required_keywords": [
|
||||||
|
"mars"
|
||||||
|
],
|
||||||
|
"excluded_keywords": [
|
||||||
|
"nose",
|
||||||
|
"fil d'ariane"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"languages": [
|
||||||
|
"en",
|
||||||
|
"fr",
|
||||||
|
"ru"
|
||||||
|
],
|
||||||
|
"countries": [],
|
||||||
|
"sources": [
|
||||||
|
"twitter",
|
||||||
|
"news",
|
||||||
|
"web",
|
||||||
|
"blogs",
|
||||||
|
"videos",
|
||||||
|
"forums",
|
||||||
|
"images"
|
||||||
|
],
|
||||||
|
"blocked_sites": [
|
||||||
|
"www.spaceoflovemagazine.com/"
|
||||||
|
],
|
||||||
|
"role": "admin",
|
||||||
|
"stats": {
|
||||||
|
"mention_sources": {
|
||||||
|
"total": "0"
|
||||||
|
},
|
||||||
|
"mention_folders": {
|
||||||
|
"inbox": {
|
||||||
|
"total": "0"
|
||||||
|
},
|
||||||
|
"archive": {
|
||||||
|
"total": "0"
|
||||||
|
},
|
||||||
|
"spam": {
|
||||||
|
"total": "0"
|
||||||
|
},
|
||||||
|
"trash": {
|
||||||
|
"total": "0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unread_mentions": {
|
||||||
|
"total": "0"
|
||||||
|
},
|
||||||
|
"unseen_mentions": {
|
||||||
|
"total": "0"
|
||||||
|
},
|
||||||
|
"favorite_mentions": {
|
||||||
|
"total": "0"
|
||||||
|
},
|
||||||
|
"important_mentions": {
|
||||||
|
"total": "0"
|
||||||
|
},
|
||||||
|
"tasks": {
|
||||||
|
"total": "0"
|
||||||
|
},
|
||||||
|
"todo_tasks": {
|
||||||
|
"total": "0"
|
||||||
|
},
|
||||||
|
"done_tasks": {
|
||||||
|
"total": "0"
|
||||||
|
},
|
||||||
|
"logs": {
|
||||||
|
"total": "0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"shares": [
|
||||||
|
{
|
||||||
|
"id": "THE_ACCOUNT_ID",
|
||||||
|
"account": {
|
||||||
|
"id": "THE_ACCOUNT_ID",
|
||||||
|
"name": "Doe",
|
||||||
|
"email": "john.doe@nasa.com",
|
||||||
|
"language_code": "en",
|
||||||
|
"created_at": "2014-09-30T10:03:54.0+00:00",
|
||||||
|
"updated_at": "2016-01-14T14:55:57.0+00:00",
|
||||||
|
"avatar_url": "https:\/\/d39qsljf883l.cloudfront.net\/f6415b89ef2ljkca5c0c7d464f1b82-088f3dsqlj12lj4.jpg",
|
||||||
|
"timezone": "Europe\/Berlin",
|
||||||
|
"grouped_email_notification": true,
|
||||||
|
"default_email_notification_frequency": "daily",
|
||||||
|
"default_desktop_notification_frequency": "hourly",
|
||||||
|
"default_push_notification_frequency": "hourly"
|
||||||
|
},
|
||||||
|
"role": "admin",
|
||||||
|
"permissions": {
|
||||||
|
"edit": true,
|
||||||
|
"delete": true
|
||||||
|
},
|
||||||
|
"created_at": "2016-01-14T15:31:42.0+00:00",
|
||||||
|
"weight": 88674800
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"noise_detection": true,
|
||||||
|
"created_at": "2016-01-14T15:31:42.0+00:00",
|
||||||
|
"updated_at": "2016-01-14T15:31:44.0+00:00",
|
||||||
|
"quota_used": 0,
|
||||||
|
"index_version": 2,
|
||||||
|
"permissions": {
|
||||||
|
"edit": true,
|
||||||
|
"share": true,
|
||||||
|
"list_tasks": true,
|
||||||
|
"list_logs": true
|
||||||
|
},
|
||||||
|
"description": "Monitor NASA press release.",
|
||||||
|
"color": "#05e363",
|
||||||
|
"connection_type": "related",
|
||||||
|
"connection_id": "12121212"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"mentions": [
|
||||||
|
{...mention...},
|
||||||
|
{...mention...},
|
||||||
|
{...mention...}
|
||||||
|
],
|
||||||
|
"_links": {
|
||||||
|
"more": {
|
||||||
|
"href": "...url to get older mentions..."
|
||||||
|
},
|
||||||
|
"pull": {
|
||||||
|
"href": "...url to get newer mentions..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
73
zulip_bots/zulip_bots/bots/mention/fixtures/mention.json
Normal file
73
zulip_bots/zulip_bots/bots/mention/fixtures/mention.json
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
{
|
||||||
|
"id": "527849933",
|
||||||
|
"alert_id": 112233,
|
||||||
|
"title": "NASA image shows 'starburst spider' pattern on Mars",
|
||||||
|
"description": "USA TODAY NASA image shows 'starburst spider' pattern on Mars USA TODAY NASA's Mars Reconnaissance Orbiter captured a stunning image of a \"starburst\" pattern on Mars ' surface. The \u201cstarbursts\u201d can be seen each spring when Mars ' seasonal cap of carbon \u2026",
|
||||||
|
"original_url": "http:\/\/www.usatoday.com\/story\/tech\/nation-now\/2016\/01\/14\/nasa-image-shows-starburst-spider-pattern-mars\/78781074\/",
|
||||||
|
"clickable_url": "https:\/\/web.mention.com\/api\/url?token=eyJ0eXAiOiJKV1QiLChbGciOiJIUzI1NiJ9.eyJ1cmwiOJodHRwOlwvXC93d3cudXNhdG9kYkuY29tXC9zdG9eVwvdGVjaFwvbmF0a9uLW5vd1wvMjAxNlwvMDFcLzE0XC9uYXNLWltYWdlLXNob3dzLXN0YJidXJzdC1zcGlZXItcGF0dGVyb1tYXJzXC83ODc4MTA3NFwvIiwiYWNjb3VudF9pZI6NDM4NDA0LCJhbGVydF9pZI6MTE2NTk5NSwic291cmNlXlkIjo0LCJtZW50a9uX2lkIjoiNzU2NzM5MjMzMIifQ.XH7WJlYkOYTDFysZELmouro__7QVoe5pT9c1qeZw",
|
||||||
|
"displayable_url": "usatoday.com\/story\/tech\/nation-now\/2016\/01\/14\/nasa-image-shows-starburst-spider-pattern-mars\/78781074\/",
|
||||||
|
"unique_id": "http:\/\/www.usatoday.com\/story\/tech\/nation-now\/2016\/01\/14\/nasa-image-shows-starburst-spider-pattern-mars\/78781074\/",
|
||||||
|
"published_at": "2016-01-14T17:16:27.10090700+00:00",
|
||||||
|
"created_at": "2016-01-18T16:05:44.0+00:00",
|
||||||
|
"country": "US",
|
||||||
|
"updated_at": "2016-01-18T16:05:45.0+00:00",
|
||||||
|
"favorite": false,
|
||||||
|
"folder": "inbox",
|
||||||
|
"folder_set_by_user": false,
|
||||||
|
"read": false,
|
||||||
|
"tone": 0,
|
||||||
|
"source_type": "news",
|
||||||
|
"source_name": "USA TODAY",
|
||||||
|
"source_url": "http:\/\/www.usatoday.com\/",
|
||||||
|
"language_code": "en",
|
||||||
|
"tasks": [],
|
||||||
|
"logs": [],
|
||||||
|
"children": {
|
||||||
|
"children": [
|
||||||
|
{...mention...},
|
||||||
|
{...mention...},
|
||||||
|
{...mention...}
|
||||||
|
],
|
||||||
|
"total": 42,
|
||||||
|
"_links": {
|
||||||
|
"more": {
|
||||||
|
"href": "/api/accounts/THE_ACCOUNT_ID/alerts/112233/mentions/527849937/children?limit=20&before_date=2014-03-20T18:10:37.53829200+00:00",
|
||||||
|
"params": {
|
||||||
|
"limit": 20,
|
||||||
|
"before_date": "2014-03-20T18:10:37.53829200+00:00"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"picture_url": "\/\/t3.gstatic.com\/images?q=tbn:ANd9GcQlW3QxiNh2YxxacyF0gR636ViYH6YS_0ONIRj9pf4OhiRZ8hHHyCQqdOYVgMuToZ1Iixhy",
|
||||||
|
"tags": [],
|
||||||
|
"offsets": {
|
||||||
|
"title": [0, 0, 4, 4, 47, 47, 4, 4],
|
||||||
|
"description": [10,10, 4, 4, 57, 57, 4, 4, 72, 72, 4, 4, 79, 79, 4, 4, 161, 161, 4, 4, 223, 223, 4, 4],
|
||||||
|
"url": [],
|
||||||
|
"source_name": [],
|
||||||
|
"source_url": []
|
||||||
|
},
|
||||||
|
"permissions": {
|
||||||
|
"favorite": true,
|
||||||
|
"change_folder": true,
|
||||||
|
"create_task": true
|
||||||
|
},
|
||||||
|
"author_influence": {
|
||||||
|
"id": "3153793048",
|
||||||
|
"alert_id": 112233,
|
||||||
|
"kind": "web",
|
||||||
|
"url": "http:\/\/www.usatoday.com",
|
||||||
|
"name": "usatoday.com",
|
||||||
|
"score": 83,
|
||||||
|
"scored_id": "usatoday.com"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"twitter": {
|
||||||
|
"id_str": "800747234652340224",
|
||||||
|
"user": {
|
||||||
|
"id_str": "188302352"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
zulip_bots/zulip_bots/bots/mention/mention.conf
Normal file
3
zulip_bots/zulip_bots/bots/mention/mention.conf
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[mention]
|
||||||
|
access_token = <access_token>
|
||||||
|
# create an app here and get the access token from it: https://dev.mention.com/login
|
103
zulip_bots/zulip_bots/bots/mention/mention.py
Normal file
103
zulip_bots/zulip_bots/bots/mention/mention.py
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
# See readme.md for instructions on running this code.
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from typing import Any, List
|
||||||
|
|
||||||
|
class MentionHandler(object):
|
||||||
|
def initialize(self, bot_handler: Any) -> None:
|
||||||
|
self.config_info = bot_handler.get_config_info('mention')
|
||||||
|
self.access_token = self.config_info['access_token']
|
||||||
|
self.account_id = ''
|
||||||
|
|
||||||
|
def usage(self) -> str:
|
||||||
|
return '''
|
||||||
|
This is a Mention API Bot which will find mentions
|
||||||
|
of the given keyword throughout the web.
|
||||||
|
Version 1.00
|
||||||
|
'''
|
||||||
|
|
||||||
|
def handle_message(self, message: Any, bot_handler: Any) -> None:
|
||||||
|
message['content'] = message['content'].strip()
|
||||||
|
|
||||||
|
if message['content'].lower() == 'help':
|
||||||
|
bot_handler.send_reply(message, self.usage())
|
||||||
|
return
|
||||||
|
|
||||||
|
if message['content'] == '':
|
||||||
|
bot_handler.send_reply(message, 'Empty Mention Query')
|
||||||
|
return
|
||||||
|
|
||||||
|
keyword = message['content']
|
||||||
|
content = self.generate_response(keyword)
|
||||||
|
bot_handler.send_reply(message, content)
|
||||||
|
|
||||||
|
def get_account_id(self) -> str:
|
||||||
|
get_ac_id_header = {
|
||||||
|
'Authorization': 'Bearer ' + self.access_token,
|
||||||
|
'Accept-Version': '1.15',
|
||||||
|
}
|
||||||
|
response = requests.get(
|
||||||
|
'https://api.mention.net/api/accounts/me', headers=get_ac_id_header)
|
||||||
|
data_json = response.json()
|
||||||
|
account_id = data_json['account']['id']
|
||||||
|
return account_id
|
||||||
|
|
||||||
|
def get_alert_id(self, keyword: str) -> str:
|
||||||
|
create_alert_header = {
|
||||||
|
'Authorization': 'Bearer ' + self.access_token,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept-Version': '1.15',
|
||||||
|
}
|
||||||
|
|
||||||
|
create_alert_data = {
|
||||||
|
'name': keyword,
|
||||||
|
'query': {
|
||||||
|
'type': 'basic',
|
||||||
|
'included_keywords': [keyword]
|
||||||
|
},
|
||||||
|
'languages': ['en'],
|
||||||
|
'sources': ['web']
|
||||||
|
} # type: Any
|
||||||
|
|
||||||
|
response = requests.post('https://api.mention.net/api/accounts/' + self.account_id +
|
||||||
|
'/alerts', data=create_alert_data, headers=create_alert_header)
|
||||||
|
data_json = response.json()
|
||||||
|
alert_id = data_json['alert']['id']
|
||||||
|
return alert_id
|
||||||
|
|
||||||
|
def get_mentions(self, alert_id: str) -> List[Any]:
|
||||||
|
get_mentions_header = {
|
||||||
|
'Authorization': 'Bearer ' + self.access_token,
|
||||||
|
'Accept-Version': '1.15',
|
||||||
|
}
|
||||||
|
response = requests.get('https://api.mention.net/api/accounts/' + self.account_id +
|
||||||
|
'/alerts/' + alert_id + '/mentions', headers=get_mentions_header)
|
||||||
|
data_json = response.json()
|
||||||
|
mentions = data_json['mentions']
|
||||||
|
return mentions
|
||||||
|
|
||||||
|
def generate_response(self, keyword: str) -> str:
|
||||||
|
if self.account_id == '':
|
||||||
|
self.account_id = self.get_account_id()
|
||||||
|
|
||||||
|
try:
|
||||||
|
alert_id = self.get_alert_id(keyword)
|
||||||
|
except (TypeError, KeyError):
|
||||||
|
# Usually triggered by invalid token or json parse error when account quote is finished.
|
||||||
|
raise MentionNoResponseException()
|
||||||
|
|
||||||
|
try:
|
||||||
|
mentions = self.get_mentions(alert_id)
|
||||||
|
except (TypeError, KeyError):
|
||||||
|
# Usually triggered by no response or json parse error when account quota is finished.
|
||||||
|
raise MentionNoResponseException()
|
||||||
|
|
||||||
|
reply = 'The most recent mentions of `' + keyword + '` on the web are: \n'
|
||||||
|
for mention in mentions:
|
||||||
|
reply += "[{title}]({id})\n".format(title=mention['title'], id=mention['original_url'])
|
||||||
|
return reply
|
||||||
|
|
||||||
|
handler_class = MentionHandler
|
||||||
|
|
||||||
|
class MentionNoResponseException(Exception):
|
||||||
|
pass
|
34
zulip_bots/zulip_bots/bots/mention/test_mention.py
Normal file
34
zulip_bots/zulip_bots/bots/mention/test_mention.py
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
from zulip_bots.test_lib import BotTestCase
|
||||||
|
|
||||||
|
class TestMentionBot(BotTestCase):
|
||||||
|
bot_name = "mention"
|
||||||
|
|
||||||
|
def test_bot_responds_to_empty_message(self) -> None:
|
||||||
|
# Offline query.
|
||||||
|
with self.mock_config_info({'access_token': '12345'}):
|
||||||
|
self.verify_reply('', 'Empty Mention Query')
|
||||||
|
|
||||||
|
def test_help_query(self) -> None:
|
||||||
|
# Offline query.
|
||||||
|
with self.mock_config_info({'access_token': '12345'}):
|
||||||
|
self.verify_reply('help', '''
|
||||||
|
This is a Mention API Bot which will find mentions
|
||||||
|
of the given keyword throughout the web.
|
||||||
|
Version 1.00
|
||||||
|
''')
|
||||||
|
|
||||||
|
# Offline query.
|
||||||
|
with self.mock_config_info({'access_token': '12345'}):
|
||||||
|
self.verify_reply('hElp', '''
|
||||||
|
This is a Mention API Bot which will find mentions
|
||||||
|
of the given keyword throughout the web.
|
||||||
|
Version 1.00
|
||||||
|
''')
|
||||||
|
|
||||||
|
# Offline query.
|
||||||
|
with self.mock_config_info({'access_token': '12345'}):
|
||||||
|
self.verify_reply('HELP', '''
|
||||||
|
This is a Mention API Bot which will find mentions
|
||||||
|
of the given keyword throughout the web.
|
||||||
|
Version 1.00
|
||||||
|
''')
|
Loading…
Reference in a new issue