Source code for nefelibata.announcers.twitter
import logging
import urllib.parse
from datetime import timezone
from pathlib import Path
from typing import Any
from typing import Dict
from typing import List
import dateutil.parser
import twitter
from nefelibata.announcers import Announcer
from nefelibata.announcers import Response
from nefelibata.post import Post
_logger = logging.getLogger(__name__)
max_length = 280
[docs]def get_response_from_mention(tweet: Dict[str, Any]) -> Response:
"""Generate a standard reply from a Tweet.
"""
return {
"source": "Twitter",
"color": "#00acee",
"id": f'twitter:{tweet["id_str"]}',
"timestamp": dateutil.parser.parse(tweet["created_at"])
.astimezone(timezone.utc)
.isoformat(),
"user": {
"name": tweet["user"]["name"],
"image": tweet["user"]["profile_image_url_https"],
"url": tweet["user"]["url"],
"description": tweet["user"]["description"],
},
"comment": {
"text": tweet["text"],
"url": f'https://twitter.com/{tweet["user"]["screen_name"]}/status/{tweet["id_str"]}',
},
}
[docs]class TwitterAnnouncer(Announcer):
"""A Twitter announcer/collector.
The configuration in nefelibata.yaml should look like this:
twitter:
consumer_key: XXX
consumer_secret: XXX
oauth_token: XXX
oauth_secret: XXX
In order to create consumer_key and consumer_secret you must visit
https://dev.twitter.com/apps/new and register a new application. Then
you should run the following script to get oauth_token and oauth_secret:
import os
from twitter import *
CONSUMER_KEY = "XXX"
CONSUMER_SECRET = "XXX"
oauth_dance("My App Name", CONSUMER_KEY, CONSUMER_SECRET, "secret.txt")
After running the script and following the instructions oauth_token and
oauth_secret will be stored in `secret.txt`.
"""
id = "twitter"
name = "Twitter"
url_header = "twitter-url"
def __init__(
self,
root: Path,
config: Dict[str, Any],
handle: str,
consumer_key: str,
consumer_secret: str,
oauth_token: str,
oauth_secret: str,
):
super().__init__(root, config)
self.oauth_token = oauth_token
self.oauth_secret = oauth_secret
self.consumer_key = consumer_key
self.consumer_secret = consumer_secret
[docs] def announce(self, post: Post) -> str:
"""Publish the summary of a post to Twitter.
"""
_logger.info("Posting to Twitter")
auth = twitter.OAuth(
self.oauth_token,
self.oauth_secret,
self.consumer_key,
self.consumer_secret,
)
client = twitter.Twitter(auth=auth)
post_url = urllib.parse.urljoin(self.config["url"], post.url)
if "twitter_card" in self.config["assistants"]:
status = post_url
else:
status = f"{post.summary[: max_length - 1 - len(post_url)]} {post_url}"
response = client.statuses.update(status=status)
_logger.info("Success!")
return f'https://twitter.com/{response["user"]["screen_name"]}/status/{response["id_str"]}'
[docs] def collect(self, post: Post) -> List[Response]:
"""Collect responses to a given tweet.
Amazingly there's no support in the API to fetch all replies to a given
tweet id, so we need to fetch all mentions and see which of them are
a reply.
"""
_logger.info("Collecting replies from Twitter")
auth = twitter.OAuth(
self.oauth_token,
self.oauth_secret,
self.consumer_key,
self.consumer_secret,
)
client = twitter.Twitter(auth=auth)
tweet_url = post.parsed[self.url_header]
tweet_id = tweet_url.rstrip("/").rsplit("/", 1)[1]
try:
mentions = client.statuses.mentions_timeline(
count=200,
since_id=tweet_id,
trim_user=False,
contributor_details=True,
include_entities=True,
)
except Exception:
return []
responses = []
for mention in mentions:
if mention["in_reply_to_status_id_str"] == tweet_id:
response = get_response_from_mention(mention)
response["url"] = tweet_url
responses.append(response)
_logger.info("Success!")
return responses