This repository was archived by the owner on Dec 4, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrender.py
More file actions
132 lines (105 loc) · 3.87 KB
/
render.py
File metadata and controls
132 lines (105 loc) · 3.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import re
import random
import tweepy
from urlsbystate import URLS_BY_STATE, CITIES
from transport import api
TWITTER_SHORT_URL_LENGTH = len('https://t.co/XXXXXXXXXX?amp=1') # XXX By observation only.
CHARACTER_LIMIT = 280 # XXX Can't find a constant in tweepy.Client
def hashtag(phrase, plain=False):
"""
Generate hashtags from phrases. Camelcase the resulting hashtag, strip punct.
Allow suppression of style changes, e.g. for two-letter state codes.
"""
words = phrase.split(' ')
if not plain:
for i in range(len(words)):
try:
if not words[i]:
del words[i]
words[i] = words[i][0].upper() + words[i][1:]
words[i] = re.sub(r"['./-]", "", words[i])
except IndexError:
break
return '#' + ''.join(words)
def build_voterinfo(campaign, state_name):
"""Render a tweet of voting info for a state"""
state = campaign.info_by_state[state_name]
num_cities = len(state[CITIES])
assert num_cities == len(set(state[CITIES])), f"Duplicate entries in CITIES for {state_name}."
city_ct = 0
effective_length = 0
tweet_text = ""
while city_ct < num_cities:
# Iterate on building a tweet until it fits within the limit.
# Return none if unsuccessful
city_set = set(state[CITIES])
try:
# Select up to city_ct cities
cities = []
cities_found = 0
while cities_found < city_ct:
city_idx = select_city(campaign, state, num_cities)
city = state[CITIES][city_idx]
if city in city_set:
cities.append(hashtag(city))
city_set.remove(city)
cities_found += 1
effective_length, tweet_text = render_voterinfo(campaign, state_name, cities)
city_ct += 1
except AssertionError:
# The last-returned tweet_text is at "max" capacity
break
return effective_length, tweet_text
def select_city(campaign, state_info, num_cities):
# Simple function to pick a random index into the list of cities based on normal distribution of indexes.
if campaign.CITIES_DISTRO in state_info:
city_idx = (num_cities >> 1) + int(round(random.gauss(0.0, state_info[campaign.CITIES_DISTRO] * num_cities)))
city_idx = min(max(0, city_idx), num_cities - 1)
else:
city_idx = random.randint(0, num_cities - 1)
# print(f"city_idx: {city_idx}")
return city_idx
def render_tweet(tweet: list):
return '\n'.join(tweet[2:]) if tweet else ""
def render_voterinfo(campaign, state_name, cities):
tweet = campaign.build_tweet(state=state_name, cities=cities)
tweet_text = render_tweet(tweet)
# Now try to guess the length of the resulting tweet. Twitter imposes the
# length limit after it shortens the links.
effective_length = len(tweet_text) - tweet[0] + tweet[1] * TWITTER_SHORT_URL_LENGTH if tweet else 0
assert effective_length <= CHARACTER_LIMIT
return effective_length, tweet_text
def build_socialize(campaign, user_id):
# Build a tweet asking for retweets
try:
screen_name = api.get_user(id=user_id).data.username
print(f"Socialize: {screen_name}")
except tweepy.errors.HTTPException as e:
print(f"Twitter user {user_id} not found ({str(e)}).")
return 0, ""
tweet = campaign.build_tweet(screen_name=screen_name)
tweet_text = render_tweet(tweet)
# Now try to guess the length of the resulting tweet. Twitter imposes the
# length limit after it shortens the links.
effective_length = len(tweet_text) - tweet[0] + tweet[1] * TWITTER_SHORT_URL_LENGTH if tweet else 0
# print(effective_length, tweet_text)
assert effective_length <= CHARACTER_LIMIT
return effective_length, tweet_text
def shell_string(chars):
if ' ' in chars:
# Ensure shell keeps words in a string
return f'"{chars}" '
else:
return f'{chars} '
# For bot-driver script
def all_states(campaign=None):
if campaign is None:
keys = URLS_BY_STATE.keys()
else:
keys = campaign.info_by_state.keys()
for state in keys:
print(shell_string(state))
# For bot-driver script
def all_cities(campaign, state):
for city in campaign.info_by_state[state][CITIES]:
print(shell_string(city))