Add twitpost bot.
This commit is contained in:
parent
b6afa030c5
commit
a9a988f726
44
zulip_bots/zulip_bots/bots/twitpost/Readme.md
Normal file
44
zulip_bots/zulip_bots/bots/twitpost/Readme.md
Normal file
|
@ -0,0 +1,44 @@
|
|||
# Twitpost Bot
|
||||
|
||||
Twitpost bot is a Zulip bot to tweet from zulip chat.
|
||||
|
||||
To use twitpost bot, you can simply call it with `@twitpost` followed
|
||||
by a keyword `tweet` followed by the content to be tweeted.
|
||||
For example:
|
||||
|
||||
`@twitpost tweet hey batman`
|
||||
|
||||
# Setup
|
||||
|
||||
Before starting using the bot, you will need:
|
||||
|
||||
1. consumer_key
|
||||
2. consumer_secret
|
||||
3. access_token
|
||||
4. access_token_secret
|
||||
|
||||
To obtain these 4 keys, follow the following steps :
|
||||
|
||||
1. Login on [Twitter Application Management](https://apps.twitter.com/) using your Twitter account credentials.
|
||||
2. Create a new Twitter app in the [Twitter Application Management](https://apps.twitter.com/)
|
||||
3. Provide the required details (Name, Description and Website).
|
||||
4. Open your app and click on `Keys and Access Tokens`.
|
||||
5. This completes creation of Twitter app to get the 4 required keys.
|
||||
6. Take a look at configuration section to configure the bot.
|
||||
|
||||
# Configuration
|
||||
|
||||
Enter the 4 keys in the respective field in `twitter.ini` file.
|
||||
|
||||
Run this bot as described in [here](https://zulipchat.com/api/running-bots#running-a-bot).
|
||||
|
||||
## Usage
|
||||
|
||||
`@twitpost tweet`
|
||||
|
||||
- This command tweets the given content to Twitter.
|
||||
- Example Usage: `@twitpost tweet hey batman`, `@twitpost tweet hello world!`
|
||||
- Sample Output:
|
||||
|
||||
`Tweet Posted
|
||||
https://twitter.com/jasoncosta/status/243145735212777472`
|
|
@ -0,0 +1,91 @@
|
|||
{
|
||||
"coordinates": null,
|
||||
"favorited": false,
|
||||
"created_at": "Wed Sep 05 00:37:15 +0000 2012",
|
||||
"truncated": false,
|
||||
"id_str": "243145735212777472",
|
||||
"entities": {
|
||||
"urls": [],
|
||||
"hashtags": [
|
||||
{
|
||||
"text": "peterfalk",
|
||||
"indices": [
|
||||
35,
|
||||
45
|
||||
]
|
||||
}
|
||||
],
|
||||
"user_mentions": []
|
||||
},
|
||||
"in_reply_to_user_id_str": null,
|
||||
"text": "Maybe he'll finally find his keys. #peterfalk",
|
||||
"contributors": null,
|
||||
"retweet_count": 0,
|
||||
"id": 243145735212777472,
|
||||
"in_reply_to_status_id_str": null,
|
||||
"geo": null,
|
||||
"retweeted": false,
|
||||
"in_reply_to_user_id": null,
|
||||
"place": null,
|
||||
"user": {
|
||||
"name": "Jason Costa",
|
||||
"profile_sidebar_border_color": "86A4A6",
|
||||
"profile_sidebar_fill_color": "A0C5C7",
|
||||
"profile_background_tile": false,
|
||||
"profile_image_url": "http://a0.twimg.com/profile_images/1751674923/new_york_beard_normal.jpg",
|
||||
"created_at": "Wed May 28 00:20:15 +0000 2008",
|
||||
"location": "",
|
||||
"is_translator": true,
|
||||
"follow_request_sent": false,
|
||||
"id_str": "14927800",
|
||||
"profile_link_color": "FF3300",
|
||||
"entities": {
|
||||
"url": {
|
||||
"urls": [
|
||||
{
|
||||
"expanded_url": "http://www.jason-costa.blogspot.com/",
|
||||
"url": "http://t.co/YCA3ZKY",
|
||||
"indices": [
|
||||
0,
|
||||
19
|
||||
],
|
||||
"display_url": "jason-costa.blogspot.com"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": {
|
||||
"urls": []
|
||||
}
|
||||
},
|
||||
"default_profile": false,
|
||||
"contributors_enabled": false,
|
||||
"url": "http://t.co/YCA3ZKY",
|
||||
"favourites_count": 883,
|
||||
"utc_offset": -28800,
|
||||
"id": 14927800,
|
||||
"profile_image_url_https": "https://si0.twimg.com/profile_images/1751674923/new_york_beard_normal.jpg",
|
||||
"profile_use_background_image": true,
|
||||
"listed_count": 150,
|
||||
"profile_text_color": "333333",
|
||||
"protected": false,
|
||||
"lang": "en",
|
||||
"followers_count": 8760,
|
||||
"time_zone": "Pacific Time (US & Canada)",
|
||||
"profile_background_image_url_https": "https://si0.twimg.com/images/themes/theme6/bg.gif",
|
||||
"verified": false,
|
||||
"profile_background_color": "709397",
|
||||
"notifications": false,
|
||||
"description": "Platform at Twitter",
|
||||
"geo_enabled": true,
|
||||
"statuses_count": 5532,
|
||||
"default_profile_image": false,
|
||||
"friends_count": 166,
|
||||
"profile_background_image_url": "http://a0.twimg.com/images/themes/theme6/bg.gif",
|
||||
"show_all_inline_media": true,
|
||||
"screen_name": "jasoncosta",
|
||||
"following": false
|
||||
},
|
||||
"source": "My Shiny App",
|
||||
"in_reply_to_screen_name": null,
|
||||
"in_reply_to_status_id": null
|
||||
}
|
51
zulip_bots/zulip_bots/bots/twitpost/test_twitpost.py
Normal file
51
zulip_bots/zulip_bots/bots/twitpost/test_twitpost.py
Normal file
|
@ -0,0 +1,51 @@
|
|||
from zulip_bots.test_lib import (
|
||||
StubBotHandler,
|
||||
BotTestCase,
|
||||
get_bot_message_handler,
|
||||
)
|
||||
from zulip_bots.test_file_utils import (
|
||||
read_bot_fixture_data,
|
||||
)
|
||||
from unittest.mock import patch
|
||||
import tweepy
|
||||
import os
|
||||
import json
|
||||
|
||||
|
||||
class TestTwitpostBot(BotTestCase):
|
||||
bot_name = "twitpost"
|
||||
mock_config = {'consumer_key': 'abcdefghijklmnopqrstuvwxy',
|
||||
'consumer_secret': 'aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyy',
|
||||
'access_token': '123456789012345678-ABCDefgh1234afdsa678lKj6gHhslsi',
|
||||
'access_token_secret': 'yf0SI0x6Ct2OmF0cDQc1E0eLKXrVAPFx4QkZF2f9PfFCt'}
|
||||
api_response = read_bot_fixture_data('twitpost', 'api_response')
|
||||
|
||||
def test_bot_usage(self) -> None:
|
||||
bot = get_bot_message_handler(self.bot_name)
|
||||
bot_handler = StubBotHandler()
|
||||
|
||||
with self.mock_config_info(self.mock_config):
|
||||
bot.initialize(bot_handler)
|
||||
|
||||
self.assertIn('This bot posts on twitter from zulip chat itself', bot.usage())
|
||||
|
||||
def test_bot_responds_to_empty_message(self) -> None:
|
||||
with self.mock_config_info(self.mock_config):
|
||||
self.verify_reply('', 'Please check help for usage.')
|
||||
|
||||
def test_help(self) -> None:
|
||||
with self.mock_config_info(self.mock_config):
|
||||
self.verify_reply('help',
|
||||
"*Help for Twitter-post bot* :twitter: : \n\n"
|
||||
"The bot tweets on twitter when message starts with @twitpost.\n\n"
|
||||
"`@twitpost tweet <content>` will tweet on twitter with given `<content>`.\n"
|
||||
"Example:\n"
|
||||
" * @twitpost tweet hey batman\n")
|
||||
|
||||
@patch('tweepy.API.update_status', return_value=api_response)
|
||||
def test_tweet(self, mockedarg):
|
||||
test_message = 'tweet Maybe he\'ll finally find his keys. #peterfalk'
|
||||
bot_response = 'Tweet Posted\n'\
|
||||
'https://twitter.com/jasoncosta/status/243145735212777472'
|
||||
with self.mock_config_info(self.mock_config):
|
||||
self.verify_reply(test_message, bot_response)
|
54
zulip_bots/zulip_bots/bots/twitpost/twitpost.py
Normal file
54
zulip_bots/zulip_bots/bots/twitpost/twitpost.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
import zulip
|
||||
import tweepy
|
||||
from typing import Dict, Any, Union, List, Tuple, Optional
|
||||
|
||||
|
||||
class TwitpostBot(object):
|
||||
|
||||
def usage(self) -> str:
|
||||
return ''' This bot posts on twitter from zulip chat itself.
|
||||
Use '@twitpost help' to get more information
|
||||
on the bot usage. '''
|
||||
help_content = "*Help for Twitter-post bot* :twitter: : \n\n"\
|
||||
"The bot tweets on twitter when message starts "\
|
||||
"with @twitpost.\n\n"\
|
||||
"`@twitpost tweet <content>` will tweet on twitter " \
|
||||
"with given `<content>`.\n" \
|
||||
"Example:\n" \
|
||||
" * @twitpost tweet hey batman\n"
|
||||
|
||||
def initialize(self, bot_handler: Any) -> None:
|
||||
self.config_info = bot_handler.get_config_info('twitter')
|
||||
auth = tweepy.OAuthHandler(self.config_info['consumer_key'],
|
||||
self.config_info['consumer_secret'])
|
||||
auth.set_access_token(self.config_info['access_token'],
|
||||
self.config_info['access_token_secret'])
|
||||
self.api = tweepy.API(auth, parser=tweepy.parsers.JSONParser())
|
||||
|
||||
def handle_message(self, message: Dict[str, str], bot_handler: Any) -> None:
|
||||
content = message["content"]
|
||||
|
||||
if content.strip() == '':
|
||||
bot_handler.send_reply(message, 'Please check help for usage.')
|
||||
return
|
||||
|
||||
if content.strip() == 'help':
|
||||
bot_handler.send_reply(message, self.help_content)
|
||||
return
|
||||
|
||||
content = content.split()
|
||||
|
||||
if len(content) > 1 and content[0] == "tweet":
|
||||
status = self.post(" ".join(content[1:]))
|
||||
screen_name = status["user"]["screen_name"]
|
||||
id_str = status["id_str"]
|
||||
bot_reply = "https://twitter.com/{}/status/{}".format(screen_name,
|
||||
id_str)
|
||||
bot_reply = "Tweet Posted\n" + bot_reply
|
||||
bot_handler.send_reply(message, bot_reply)
|
||||
|
||||
def post(self, text):
|
||||
return self.api.update_status(text)
|
||||
|
||||
|
||||
handler_class = TwitpostBot
|
6
zulip_bots/zulip_bots/bots/twitpost/twitter.ini
Normal file
6
zulip_bots/zulip_bots/bots/twitpost/twitter.ini
Normal file
|
@ -0,0 +1,6 @@
|
|||
[twitter]
|
||||
consumer_key=
|
||||
consumer_secret=
|
||||
access_token=
|
||||
access_token_secret=
|
||||
|
Loading…
Reference in a new issue