Passed
Push — master ( cc3949...eaebec )
by Ian
05:52
created

build.rsudp.c_tweet.Tweeter.auth()   A

Complexity

Conditions 2

Size

Total Lines 11
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

Changes 0
Metric Value
eloc 9
dl 0
loc 11
ccs 3
cts 4
cp 0.75
rs 9.95
c 0
b 0
f 0
cc 2
nop 1
crap 2.0625
1 1
import os, sys
2 1
import time
3 1
import rsudp.raspberryshake as rs
4 1
from rsudp import printM, printW, printE, helpers
5 1
from rsudp.test import TEST
6 1
from twython import Twython
7
8
9 1
class Tweeter(rs.ConsumerThread):
10
	'''
11
	.. versionadded:: 1.0.2
12
13
		The option to add extra text to tweets using the :code:`"extra_text"`
14
		setting in settings files built by this version and later.
15
16
	.. |raspishakeq| raw:: html
17
18
		<a href="https://twitter.com/raspishakeq" target="_blank">Raspberry Shake</a>
19
20
	.. |usgsbigquakes| raw:: html
21
22
		<a href="https://twitter.com/USGSBigQuakes" target="_blank">USGS</a>
23
24
	.. |emsc_lastquake| raw:: html
25
26
		<a href="https://twitter.com/LastQuake" target="_blank">EMSC</a>
27
28
	Twitter is a social media platform sometimes used for quickly
29
	distributing public alert information. It is used by many agencies
30
	including |raspishakeq|, |usgsbigquakes|, and |emsc_lastquake|.
31
32
	.. |tw_api_bots| raw:: html
33
34
		<a href="https://developer.twitter.com/apps" target="_blank">API bots</a>
35
36
	.. note::
37
		Twitter is more difficult and stricter when it comes to making
38
		|tw_api_bots| than many services.
39
		First, you must go through a relatively rigorous process of applying for
40
		a developer account, then making a Twitter "app", and then giving the app
41
		permission to post on your behalf. See :ref:`setting-up-twitter` for details.
42
43
		Once you've gone through that process, Twitter limits posting and makes
44
		its rules on rate limiting relatively difficult to nail down.
45
		Generally, the early 2020 rate limit for posting is 300 in a 3-hour span,
46
		however that can vary depending on whether or not Twitter thinks there is
47
		suspicious activity coming from your account.
48
49
		In general, if you are looking for a simple multi-platform notification
50
		service, it may be easier and more reliable to use the Telegram service
51
		instead. rsudp has a telegram module at
52
		:py:class:`rsudp.c_telegram.Telegram`. See :ref:`setting-up-telegram` for details.
53
54
	:param str consumer_key: Twitter calls this the "consumer key"
55
	:param str consumer_secret: Twitter calls this the "consumer key secret"
56
	:param str access_token: Twitter calls this the "consumer access token"
57
	:param str access_secret: Twitter calls this the "consumer access secret"
58
	:param bool tweet_images: whether or not to send images. if False, only alerts will be sent.
59
	:type extra_text: bool or str
60
	:param extra_text: 103 additional characters to post as part of the twitter message.
61
	:param queue.Queue q: queue of data and messages sent by :class:`rsudp.c_consumer.Consumer`
62
63
64
	'''
65 1
	def __init__(self, consumer_key, consumer_secret, access_token, access_token_secret,
66
				 q=False, tweet_images=False, extra_text=False, testing=False,
67
				 ):
68
		"""
69
		Initialize the process
70
		"""
71 1
		super().__init__()
72 1
		self.queue = q
73 1
		self.sender = 'Tweeter'
74 1
		self.alive = True
75 1
		self.tweet_images = tweet_images
76 1
		self.testing = testing
77 1
		self.fmt = '%Y-%m-%d %H:%M:%S.%f'
78 1
		self.region = ' - region: %s' % rs.region.title() if rs.region else ''
79 1
		self.consumer_key = consumer_key
80 1
		self.consumer_secret = consumer_secret
81 1
		self.access_token = access_token
82 1
		self.access_token_secret = access_token_secret
83 1
		self.last_message = False
84
85 1
		self._resolve_extra_text(extra_text)
86
87 1
		self.auth()
88
89 1
		self.livelink = u'live feed ➡️ https://stationview.raspberryshake.org/#?net=%s&sta=%s' % (rs.net, rs.stn)
90 1
		self.message0 = '(#RaspberryShake station %s.%s%s) Event detected at' % (rs.net, rs.stn, self.region)
91 1
		self.message1 = '(#RaspberryShake station %s.%s%s) Image of event detected at' % (rs.net, rs.stn, self.region)
92
93 1
		printM('Starting.', self.sender)
94
95
96 1 View Code Duplication
	def _resolve_extra_text(self, extra_text):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
97 1
		allowable_len = 103	# length of string allowable given maximum message text & region
98 1
		if ((extra_text == '') or (extra_text == None) or (extra_text == False)):
99 1
			self.extra_text = ''
100
		else:
101
			extra_text = str(extra_text)
102
			len_ex_txt = len(extra_text)
103
104
			if len_ex_txt > allowable_len:
105
				printW('extra_text parameter is longer than allowable (%s chars) and will be truncated. Please keep extra_text at or below %s characters.' % (len_ex_txt, allowable_len), sender=self.sender)
106
				extra_text = extra_text[:allowable_len]
107
108
			self.extra_text =  ' %s' % (extra_text)
109
110
111 1
	def auth(self):
112 1
		if not self.testing:
113
			self.twitter = Twython(
114
				self.consumer_key,
115
				self.consumer_secret,
116
				self.access_token,
117
				self.access_token_secret
118
			)
119
		else:
120 1
			printW('The Twitter module will not post to Twitter in Testing mode.',
121
					self.sender, announce=False)
122
123
124 1
	def getq(self):
125 1
		d = self.queue.get()
126 1
		self.queue.task_done()
127
128 1
		if 'TERM' in str(d):
129 1
			self.alive = False
130 1
			printM('Exiting.', self.sender)
131 1
			sys.exit()
132
		else:
133 1
			return d
134
135
136 1
	def _when_alarm(self, d):
137
		'''
138
		Send a tweet when you get an ``ALARM`` message.
139
140
		:param bytes d: queue message
141
		'''
142 1
		event_time = helpers.fsec(helpers.get_msg_time(d))
143 1
		self.last_event_str = '%s' % (event_time.strftime(self.fmt)[:22])
144 1
		message = '%s %s UTC%s - %s' % (self.message0, self.last_event_str, self.extra_text, self.livelink)
145 1
		response = None
146 1
		try:
147 1
			printM('Tweet: %s' % (message), sender=self.sender)
148 1
			if not self.testing:
149
				response = self.twitter.update_status(status=message, lat=rs.inv[0][0].latitude,
150
														long=rs.inv[0][0].longitude,
151
														geo_enabled=True, display_coordinates=True)
152
														# location will only stick to tweets on accounts that have location enabled in Settings
153
				url = 'https://twitter.com/%s/status/%s' % (response['user']['screen_name'], response['id_str'])
154
				printM('Tweet URL: %s' % url)
155 1
			if self.testing:
156 1
				TEST['c_tweet'][1] = True
157
158
		except Exception as e:
159
			printE('could not send alert tweet - %s' % (e))
160
			try:
161
				printE('Waiting 5 seconds and trying to send tweet again...', sender=self.sender, spaces=True)
162
				time.sleep(5.1)
163
				printM('Tweet: %s' % (message), sender=self.sender)
164
				if not self.testing:
165
					self.auth()
166
					response = self.twitter.update_status(status=message, lat=rs.inv[0][0].latitude,
167
															long=rs.inv[0][0].longitude,
168
															geo_enabled=True, display_coordinates=True)
169
															# location will only stick to tweets on accounts that have location enabled in Settings
170
					url = 'https://twitter.com/%s/status/%s' % (response['user']['screen_name'], response['id_str'])
171
					printM('Tweet URL: %s' % url)
172
			except Exception as e:
173
				printE('could not send alert tweet - %s' % (e))
174
				response = None
175
176 1
		self.last_message = message
177
178
179
180 1
	def _when_img(self, d):
181
		'''
182
		Send a tweet with an image in when you get an ``IMGPATH`` message.
183
184
		:param bytes d: queue message
185
		'''
186 1
		if self.tweet_images:
187 1
			imgpath = helpers.get_msg_path(d)
188 1
			imgtime = helpers.fsec(helpers.get_msg_time(d))
189 1
			message = '%s %s UTC%s' % (self.message1, imgtime.strftime(self.fmt)[:22], self.extra_text)
190 1
			response = None
191 1
			printM('Image tweet: %s' % (message), sender=self.sender)
192 1
			if not self.testing:
193
				if os.path.exists(imgpath):
194
					with open(imgpath, 'rb') as image:
195
						try:
196
							printM('Uploading image to Twitter %s' % (imgpath), self.sender)
197
							response = self.twitter.upload_media(media=image)
198
							time.sleep(5.1)
199
							printM('Sending tweet...', sender=self.sender)
200
							response = self.twitter.update_status(status=message, media_ids=response['media_id'],
201
																	lat=rs.inv[0][0].latitude, long=rs.inv[0][0].longitude,
202
																	geo_enabled=True, display_coordinates=True)
203
																	# location will only stick to tweets on accounts that have location enabled in Settings
204
							url = 'https://twitter.com/%s/status/%s' % (response['user']['screen_name'], response['id_str'])
205
							printM('Tweet URL: %s' % url)
206
						except Exception as e:
207
							printE('could not send multimedia tweet - %s' % (e))
208
							try:
209
								printM('Waiting 5 seconds and trying to send tweet again...', sender=self.sender)
210
								time.sleep(5.1)
211
								self.auth()
212
								printM('Uploading image to Twitter (2nd try) %s' % (imgpath), self.sender)
213
								response = self.twitter.upload_media(media=image)
214
								time.sleep(5.1)
215
								printM('Sending tweet...', sender=self.sender)
216
								response = self.twitter.update_status(status=message, media_ids=response['media_id'],
217
																		lat=rs.inv[0][0].latitude, long=rs.inv[0][0].longitude,
218
																		geo_enabled=True, display_coordinates=True)
219
																		# location will only stick to tweets on accounts that have location enabled in Settings
220
								url = 'https://twitter.com/%s/status/%s' % (response['user']['screen_name'], response['id_str'])
221
								printM('Tweet URL: %s' % url)
222
223
							except Exception as e:
224
								printE('could not send multimedia tweet (2nd try) - %s' % (e))
225
								response = None
226
227
				else:
228
					printM('Could not find image: %s' % (imgpath), sender=self.sender)
229
			else:
230 1
				TEST['c_tweetimg'][1] = True
231
		
232 1
		self.last_message = message
0 ignored issues
show
introduced by
The variable message does not seem to be defined in case self.tweet_images on line 186 is False. Are you sure this can never be the case?
Loading history...
233
234 1 View Code Duplication
	def run(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
235
		"""
236
		Reads data from the queue and tweets a message if it sees an ALARM or IMGPATH message
237
		"""
238 1
		while True:
239 1
			d = self.getq()
240
241 1
			if 'ALARM' in str(d):
242 1
				self._when_alarm(d)
243
244 1
			elif 'IMGPATH' in str(d):
245
				self._when_img(d)
246