Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
@@ -1,135 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import time
|
2 |
-
from
|
3 |
-
|
4 |
-
from mastodon import Mastodon, MastodonNotFoundError # Mastodon.py
|
5 |
-
|
6 |
-
list_of_servers = ['libretooth.gr',
|
7 |
-
'tilde.zone',
|
8 |
-
'hostux.social',
|
9 |
-
'social.linux.pizza',
|
10 |
-
'nerdculture.de',
|
11 |
-
'toot.wales',
|
12 |
-
'kolektiva.social',
|
13 |
-
'noc.social',
|
14 |
-
'seo.chat',
|
15 |
-
'ioc.exchange',
|
16 |
-
'glasgow.social',
|
17 |
-
'mindly.social',
|
18 |
-
'mstdn.party',
|
19 |
-
'universeodon.com',
|
20 |
-
'learningdisability.social',
|
21 |
-
'ravenation.club',
|
22 |
-
'home.social',
|
23 |
-
'techhub.social',
|
24 |
-
'mastodon.scot',
|
25 |
-
'sfba.social',
|
26 |
-
'mastodon.sdf.org',
|
27 |
-
'mastodon.lol',
|
28 |
-
'mstdn.social',
|
29 |
-
'mas.to',
|
30 |
-
'newsie.social']
|
31 |
-
|
32 |
-
# A HTML blob of text used to define the 'login in with' button
|
33 |
-
auth_button_text = '''<a href="{}"><button class="w3-button w3-light-grey w3-padding-large w3-section " onclick="document.getElementById('download').style.display='block'">
|
34 |
-
<i class=""></i> Login with Mastodon! 🐘
|
35 |
-
</button></a><br>'''
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
def client_log_in(provided_server = None):
|
40 |
-
'''
|
41 |
-
Generates an authenticated Masterdon client for the admin user. Used for retireving the target website URL
|
42 |
-
'''
|
43 |
-
|
44 |
-
if provided_server == None:
|
45 |
-
global slider_choice
|
46 |
-
server = slider_choice
|
47 |
-
else:
|
48 |
-
server = provided_server
|
49 |
-
|
50 |
-
if server != None:
|
51 |
-
id = "WatchTower Ivory | Mastodon"
|
52 |
-
|
53 |
-
secrets = json.loads(os.getenv('secrets_json'))
|
54 |
-
|
55 |
-
client_id = secrets[server]["client_id"]
|
56 |
-
client_secret = secrets[server]["client_secret"]
|
57 |
-
password = secrets[server]["password"]
|
58 |
-
username = os.getenv('username')
|
59 |
-
|
60 |
-
|
61 |
-
mastodon = Mastodon(
|
62 |
-
client_id=client_id,client_secret=client_secret,
|
63 |
-
api_base_url='https://{}'.format(server)
|
64 |
-
)
|
65 |
-
|
66 |
-
access_token = mastodon.log_in(
|
67 |
-
username=username,
|
68 |
-
scopes=["write:blocks", "write:mutes","read:search","read:accounts"],
|
69 |
-
password=password,
|
70 |
-
redirect_uri="https://user1342-ivory.hf.space/"
|
71 |
-
)
|
72 |
-
else:
|
73 |
-
mastodon = None
|
74 |
-
|
75 |
-
return mastodon
|
76 |
-
|
77 |
-
|
78 |
-
def get_auth_url(mastodon,provided_server = None):
|
79 |
-
'''
|
80 |
-
Retrieves a URL for the user to visit to auth them with WatchTower.
|
81 |
-
:param mastodon: A admin masterdon instance.
|
82 |
-
:return: The target URL.
|
83 |
-
'''
|
84 |
|
85 |
-
|
86 |
-
|
87 |
-
server = slider_choice
|
88 |
-
else:
|
89 |
-
server = provided_server
|
90 |
|
91 |
-
|
|
|
|
|
|
|
|
|
|
|
92 |
|
93 |
-
|
94 |
-
client_secret = secrets[server]["client_secret"]
|
95 |
-
|
96 |
-
return mastodon.auth_request_url(client_id=client_id, scopes=["write:blocks", "write:mutes","read:search","read:accounts"],
|
97 |
-
redirect_uris="https://user1342-ivory.hf.space/")
|
98 |
-
|
99 |
-
|
100 |
-
def login_from_code(code):
|
101 |
-
'''
|
102 |
-
Used to create a masterdon client instance based on an authenticated user code (retrieved from the URL).
|
103 |
-
:param code: The code which will authenticate the user/ WatchTower.
|
104 |
-
:return: A masterdon client instance signed in as the user.
|
105 |
-
'''
|
106 |
-
|
107 |
-
global slider_choice
|
108 |
-
server = slider_choice
|
109 |
-
|
110 |
-
if server != None:
|
111 |
-
secrets = json.loads(os.getenv('secrets_json'))
|
112 |
-
client_id = secrets[server]["client_id"]
|
113 |
-
client_secret = secrets[server]["client_secret"]
|
114 |
-
|
115 |
-
mastodon = Mastodon(
|
116 |
-
client_id=client_id, client_secret=client_secret,
|
117 |
-
api_base_url='https://{}'.format(server)
|
118 |
-
)
|
119 |
-
|
120 |
-
mastodon.log_in(code=code,
|
121 |
-
scopes=["write:blocks", "write:mutes","read:search","read:accounts"],
|
122 |
-
redirect_uri="https://user1342-ivory.hf.space/")
|
123 |
-
else:
|
124 |
-
mastodon = None
|
125 |
-
|
126 |
-
return mastodon
|
127 |
-
|
128 |
-
|
129 |
-
# !/usr/bin/env python
|
130 |
-
# coding: utf-8
|
131 |
-
|
132 |
-
html_data = '''
|
133 |
<!DOCTYPE html>
|
134 |
<html>
|
135 |
<head>
|
@@ -152,22 +40,23 @@ img {margin-bottom: -8px;}
|
|
152 |
<div class="w3-padding-large w3-white">
|
153 |
<div class="w3-row-padding-large">
|
154 |
<div class="w3-col">
|
155 |
-
<h1 class="w3-jumbo"><b>WatchTower
|
156 |
-
<h1 class="w3-xxxlarge w3-text-
|
157 |
-
<p><span class="w3-xlarge">Scroll down to use WatchTower
|
158 |
<a href="https://www.watchtower.cartographer.one/"><button class="w3-button w3-light-grey w3-padding-large w3-section " onclick="document.getElementById('download').style.display='block'">
|
159 |
<i class=""></i> Find Out More! 💬
|
160 |
</button></a>
|
161 |
<a href="https://ko-fi.com/jamesstevenson"><button class="w3-button w3-light-grey w3-padding-large w3-section " onclick="document.getElementById('download').style.display='block'">
|
162 |
<i class=""></i> Support The Creator! ❤
|
163 |
</button></a>
|
164 |
-
<a href="https://
|
165 |
<i class=""></i> Follow Us! 🐦
|
166 |
</button></a>
|
167 |
</div>
|
168 |
</div>
|
169 |
</div>
|
170 |
|
|
|
171 |
<script>
|
172 |
// Slideshow
|
173 |
var slideIndex = 1;
|
@@ -189,8 +78,6 @@ function showDivs(n) {
|
|
189 |
}
|
190 |
</script>
|
191 |
<br>
|
192 |
-
<br>
|
193 |
-
<br>
|
194 |
</body>
|
195 |
</html>
|
196 |
|
@@ -199,50 +86,99 @@ function showDivs(n) {
|
|
199 |
# Imports
|
200 |
import json
|
201 |
import os
|
|
|
202 |
import gradio as gr
|
|
|
203 |
|
204 |
# Setup the gradio block and add some generic CSS
|
205 |
-
block = gr.Blocks(
|
206 |
-
css=".container { max-width: 800px; margin: auto; } h1 { margin: 0px; padding: 5px 0; line-height: 50px; font-size: 60pt; }.close-heading {margin: 0px; padding: 0px;} .close-heading p { margin: 0px; padding: 0px;}",
|
207 |
-
title="WatchTower")
|
208 |
|
209 |
# Chat history variable used for the chatbot prompt on the 'getting started' page.
|
210 |
chat_history = []
|
211 |
|
212 |
|
213 |
-
def get_client_from_tokens(
|
214 |
'''
|
215 |
-
This function is used for generating a
|
216 |
-
:param
|
217 |
-
:
|
|
|
218 |
'''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
219 |
|
220 |
-
|
|
|
|
|
221 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
222 |
|
223 |
def block_user(user_id, user, reason):
|
224 |
finished = False
|
225 |
blocked = True
|
226 |
attempts = 0
|
227 |
while not finished:
|
228 |
-
|
229 |
try:
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
243 |
return blocked
|
244 |
|
245 |
-
|
246 |
def block_users(client, threshold, dataset):
|
247 |
'''
|
248 |
Used for blocking a series of users based on the threshold and datasets provided. Here the users folder is used.
|
@@ -253,7 +189,6 @@ def block_users(client, threshold, dataset):
|
|
253 |
'''
|
254 |
num_users_blocked = 0
|
255 |
|
256 |
-
|
257 |
for filename in os.listdir("users"):
|
258 |
filename = os.path.join("users", filename)
|
259 |
print("File {} open".format(filename))
|
@@ -262,27 +197,37 @@ def block_users(client, threshold, dataset):
|
|
262 |
|
263 |
for user in users:
|
264 |
print("Reviewing user {}".format(user))
|
265 |
-
|
266 |
-
|
267 |
-
#
|
268 |
-
if
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
279 |
|
280 |
return num_users_blocked
|
281 |
|
282 |
|
283 |
def chat(selected_option=None, radio_score=None, url_params=None):
|
284 |
'''
|
285 |
-
This function is used to initialise blocking users once the user has authenticated with
|
286 |
:param selected_option:
|
287 |
:param radio_score:
|
288 |
:param url_params:
|
@@ -294,8 +239,8 @@ def chat(selected_option=None, radio_score=None, url_params=None):
|
|
294 |
history = []
|
295 |
|
296 |
# app id
|
297 |
-
if "
|
298 |
-
client = get_client_from_tokens(url_params["
|
299 |
if radio_score != None and selected_option != None:
|
300 |
|
301 |
if client != None:
|
@@ -311,8 +256,7 @@ def chat(selected_option=None, radio_score=None, url_params=None):
|
|
311 |
|
312 |
# Display to user, set options
|
313 |
history.append(
|
314 |
-
["Model tuned to a '{}%' threshold and is using the {} dataset.".format(radio_score,
|
315 |
-
block_type.capitalize()),
|
316 |
"{} Account blocking initialised".format(block_type.capitalize())])
|
317 |
num_users_blocked = block_users(client, radio_score, selected_option)
|
318 |
history.append(
|
@@ -347,7 +291,7 @@ target_website = None
|
|
347 |
|
348 |
def update_target_website():
|
349 |
'''
|
350 |
-
Updates the URL used to authenticate WatchTower with
|
351 |
#TODO this function is full of old code and can be optimised.
|
352 |
:return:
|
353 |
'''
|
@@ -361,18 +305,19 @@ def update_target_website():
|
|
361 |
name = "no username"
|
362 |
|
363 |
chat_history = [
|
364 |
-
["Welcome to Watchtower.".format(name), "Log in via
|
365 |
|
366 |
chatbot.value = chat_history
|
367 |
chatbot.update(value=chat_history)
|
368 |
|
369 |
-
|
370 |
get_target_website())
|
371 |
-
|
372 |
-
value=
|
373 |
get_target_website()))
|
374 |
|
375 |
-
return
|
|
|
376 |
|
377 |
|
378 |
# The below is a JS blob used to retrieve the URL params.
|
@@ -385,36 +330,31 @@ get_window_url_params = """
|
|
385 |
return [text_input, url_params];
|
386 |
}
|
387 |
"""
|
388 |
-
slider_choice = list_of_servers[0]
|
389 |
-
def update_server(choice):
|
390 |
-
print("In change server")
|
391 |
-
global slider_choice
|
392 |
-
slider_choice = choice
|
393 |
-
|
394 |
-
get_target_website()
|
395 |
-
|
396 |
-
return auth_button_text.format(get_target_website())
|
397 |
|
398 |
def get_chatbot_text():
|
399 |
-
return [('Welcome to Watchtower.', 'Log in via
|
400 |
|
401 |
-
|
402 |
-
def get_target_website(provided_server = None):
|
403 |
'''
|
404 |
-
A wrapper function used for retrieving the URL a user will use to authenticate WatchTower with
|
405 |
-
:return:
|
406 |
'''
|
|
|
|
|
|
|
|
|
|
|
407 |
|
408 |
-
|
409 |
-
return get_auth_url(mastodon, provided_server)
|
410 |
|
411 |
|
412 |
-
# The Gradio HTML component used for the 'sign in with
|
413 |
|
414 |
# The main chunk of code that uses Gradio blocks to create the UI
|
415 |
html_button = None
|
416 |
-
dropdown = None
|
417 |
with block:
|
|
|
|
|
418 |
gr.HTML('''
|
419 |
<meta name="viewport" content="width=device-width, initial-scale=1">
|
420 |
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
|
@@ -422,57 +362,51 @@ with block:
|
|
422 |
|
423 |
# todo check if user signed in
|
424 |
|
425 |
-
user_message = "Log in via
|
426 |
-
chat_history.append(["
|
427 |
gr.HTML(value=html_data)
|
428 |
with gr.Group():
|
429 |
with gr.Row().style(equal_height=True):
|
430 |
with gr.Box():
|
431 |
-
#
|
432 |
url_params = gr.JSON({}, visible=False, label="URL Params").style(
|
433 |
)
|
434 |
text_input = gr.Text(label="Input", visible=False).style()
|
435 |
text_output = gr.Text(label="Output", visible=False).style()
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
get_target_website())).style(
|
441 |
-
)
|
442 |
with gr.Row().style(equal_height=True):
|
443 |
-
radio = gr.CheckboxGroup(value=["Violent", "Hate Speech"],
|
444 |
-
|
445 |
-
interactive=False, label="Behaviour To Block").style()
|
446 |
|
447 |
-
slider = gr.Slider(value=30, interactive=True, label="Threshold Confidence Tolerance")
|
|
|
|
|
448 |
|
449 |
-
chatbot = gr.Chatbot(label="Watchtower Output", value=[('Welcome to Watchtower.',
|
450 |
-
'Log in via Mastodon and configure your blocking options above.')]).style(
|
451 |
-
color_map=["grey", "purple"])
|
452 |
|
453 |
btn = gr.Button("Run WatchTower").style(full_width=True).style()
|
454 |
-
dropdown.change(fn=update_server, inputs=[dropdown], outputs=html_button)
|
455 |
btn.click(fn=button_pressed, inputs=[slider, url_params],
|
456 |
outputs=[text_output, chatbot], _js=get_window_url_params)
|
457 |
gr.Markdown(
|
458 |
"""___
|
459 |
<p style='text-align: center'>
|
460 |
-
Created by <a href="https://
|
461 |
<br/>
|
462 |
</p>"""
|
463 |
)
|
464 |
|
465 |
-
# Setup callback for when page loads (used to set a new
|
466 |
block.__enter__()
|
467 |
block.set_event_trigger(
|
468 |
event_name="load", fn=update_target_website, inputs=None, outputs=[html_button], no_target=True
|
469 |
)
|
470 |
-
|
471 |
block.set_event_trigger(
|
472 |
event_name="load", fn=get_chatbot_text, inputs=None, outputs=[chatbot], no_target=True
|
473 |
)
|
474 |
|
475 |
-
block.attach_load_events()
|
476 |
|
477 |
# Launcg the page
|
478 |
-
block.launch(enable_queue=True)
|
|
|
1 |
+
#!/usr/bin/env python
|
2 |
+
# coding: utf-8
|
3 |
+
import json
|
4 |
+
import os
|
5 |
+
import re
|
6 |
import time
|
7 |
+
from random import random
|
8 |
+
import socket
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
|
10 |
+
from threading import Thread
|
11 |
+
from time import sleep
|
|
|
|
|
|
|
12 |
|
13 |
+
# Twitter keys
|
14 |
+
consumer_token = os.getenv('CONSUMER_TOKEN')
|
15 |
+
consumer_secret = os.getenv('CONSUMER_SECRET')
|
16 |
+
my_access_token = os.getenv('ACCESS_TOKEN')
|
17 |
+
my_access_secret = os.getenv('ACCESS_SECRET')
|
18 |
+
bearer = os.getenv('BEARER')
|
19 |
|
20 |
+
html_data = '''a
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
<!DOCTYPE html>
|
22 |
<html>
|
23 |
<head>
|
|
|
40 |
<div class="w3-padding-large w3-white">
|
41 |
<div class="w3-row-padding-large">
|
42 |
<div class="w3-col">
|
43 |
+
<h1 class="w3-jumbo"><b>WatchTower 🐦🚧</b></h1>
|
44 |
+
<h1 class="w3-xxxlarge w3-text-blue"><b>Remove Unfavorable Tweets From Your Feed </b></h1>
|
45 |
+
<p><span class="w3-xlarge">Scroll down to use WatchTower 1.0. ⬇ </span> WatchTower is a tool that identifies hate speech, misinformation, and extremist content and blocks/ mutes it from your Twitter feed. WatchTower blocks content based on it's current database, so make sure to come back regularly to ensure you're up to date! We use a queue system, which means <b> you may need to wait your turn to run WatchTower</b> - however, once you've clicked run, you can close the tab as WatchTower will continue in the background. WatchTower is simple to use: first scroll down the page and click the 'sign in with Twitter' button, then you'll be taken to the Twitter website and asked to verify yourself, after this you'll be taken back here, then simply scroll down to the bottom of the page and click run!</p>
|
46 |
<a href="https://www.watchtower.cartographer.one/"><button class="w3-button w3-light-grey w3-padding-large w3-section " onclick="document.getElementById('download').style.display='block'">
|
47 |
<i class=""></i> Find Out More! 💬
|
48 |
</button></a>
|
49 |
<a href="https://ko-fi.com/jamesstevenson"><button class="w3-button w3-light-grey w3-padding-large w3-section " onclick="document.getElementById('download').style.display='block'">
|
50 |
<i class=""></i> Support The Creator! ❤
|
51 |
</button></a>
|
52 |
+
<a href="https://twitter.com/WATCHTOWER_WEB"><button class="w3-button w3-light-grey w3-padding-large w3-section " onclick="document.getElementById('download').style.display='block'">
|
53 |
<i class=""></i> Follow Us! 🐦
|
54 |
</button></a>
|
55 |
</div>
|
56 |
</div>
|
57 |
</div>
|
58 |
|
59 |
+
<!-- Modal -->
|
60 |
<script>
|
61 |
// Slideshow
|
62 |
var slideIndex = 1;
|
|
|
78 |
}
|
79 |
</script>
|
80 |
<br>
|
|
|
|
|
81 |
</body>
|
82 |
</html>
|
83 |
|
|
|
86 |
# Imports
|
87 |
import json
|
88 |
import os
|
89 |
+
import time
|
90 |
import gradio as gr
|
91 |
+
import tweepy
|
92 |
|
93 |
# Setup the gradio block and add some generic CSS
|
94 |
+
block = gr.Blocks(css=".container { max-width: 800px; margin: auto; } h1 { margin: 0px; padding: 5px 0; line-height: 50px; font-size: 60pt; }.close-heading {margin: 0px; padding: 0px;} .close-heading p { margin: 0px; padding: 0px;}", title="WatchTower")
|
|
|
|
|
95 |
|
96 |
# Chat history variable used for the chatbot prompt on the 'getting started' page.
|
97 |
chat_history = []
|
98 |
|
99 |
|
100 |
+
def get_client_from_tokens(oauth_verifier, oauth_token):
|
101 |
'''
|
102 |
+
This function is used for generating a Tweepy client object based on Oauth verifier and token paramiters
|
103 |
+
:param oauth_verifier:
|
104 |
+
:param oauth_token:
|
105 |
+
:return: A Tweepy client object
|
106 |
'''
|
107 |
+
new_oauth1_user_handler = tweepy.OAuth1UserHandler(
|
108 |
+
consumer_token, consumer_secret,
|
109 |
+
callback="https://hf.space/embed/User1342/WatchTower/"
|
110 |
+
)
|
111 |
+
new_oauth1_user_handler.request_token = {
|
112 |
+
"oauth_token": oauth_token,
|
113 |
+
"oauth_token_secret": consumer_secret
|
114 |
+
}
|
115 |
|
116 |
+
access_token, access_token_secret = new_oauth1_user_handler.get_access_token(
|
117 |
+
oauth_verifier
|
118 |
+
)
|
119 |
|
120 |
+
their_client = tweepy.Client(
|
121 |
+
bearer_token=bearer,
|
122 |
+
consumer_key=consumer_token,
|
123 |
+
consumer_secret=consumer_secret,
|
124 |
+
access_token=access_token,
|
125 |
+
access_token_secret=access_token_secret
|
126 |
+
)
|
127 |
+
|
128 |
+
# TODO: The below is not necessary and can be removed.
|
129 |
+
global client
|
130 |
+
client = their_client
|
131 |
+
|
132 |
+
return their_client
|
133 |
|
134 |
def block_user(user_id, user, reason):
|
135 |
finished = False
|
136 |
blocked = True
|
137 |
attempts = 0
|
138 |
while not finished:
|
139 |
+
|
140 |
try:
|
141 |
+
print("preparing to block {}".format(user_id))
|
142 |
+
client.block(target_user_id=user_id)
|
143 |
+
print("User blocked")
|
144 |
+
except tweepy.errors.TooManyRequests as e:
|
145 |
+
try:
|
146 |
+
client.mute(target_user_id=user_id)
|
147 |
+
print("Could not block, so muted")
|
148 |
+
except tweepy.errors.TooManyRequests as e:
|
149 |
+
if attempts == 0:
|
150 |
+
print("waiting 15 minutes for rate limit to finish")
|
151 |
+
time.sleep(900)
|
152 |
+
attempts = attempts + 1
|
153 |
+
continue
|
154 |
+
else:
|
155 |
+
finished = True
|
156 |
+
blocked = False
|
157 |
+
continue
|
158 |
+
except tweepy.errors.BadRequest as e:
|
159 |
+
print("bad request error")
|
160 |
+
print(e)
|
161 |
+
finished = True
|
162 |
+
blocked = False
|
163 |
+
continue
|
164 |
+
except tweepy.errors.BadRequest as e:
|
165 |
+
print("bad request error")
|
166 |
+
print(e)
|
167 |
+
finished = True
|
168 |
+
blocked = False
|
169 |
+
continue
|
170 |
+
except:
|
171 |
+
time.sleep(240)
|
172 |
+
continue
|
173 |
+
#time.sleep(1)
|
174 |
+
finished = True
|
175 |
+
try:
|
176 |
+
me = client.get_me()
|
177 |
+
print("{} blocked {}, for {}".format(me.data["username"], user, reason))
|
178 |
+
except tweepy.errors.TooManyRequests as e:
|
179 |
+
print("Blocked {}, for {}".format(user, reason))
|
180 |
return blocked
|
181 |
|
|
|
182 |
def block_users(client, threshold, dataset):
|
183 |
'''
|
184 |
Used for blocking a series of users based on the threshold and datasets provided. Here the users folder is used.
|
|
|
189 |
'''
|
190 |
num_users_blocked = 0
|
191 |
|
|
|
192 |
for filename in os.listdir("users"):
|
193 |
filename = os.path.join("users", filename)
|
194 |
print("File {} open".format(filename))
|
|
|
197 |
|
198 |
for user in users:
|
199 |
print("Reviewing user {}".format(user))
|
200 |
+
|
201 |
+
if "threshold" in user:
|
202 |
+
# old type of dataset being used, only 'violent' data available
|
203 |
+
if "Violent" in dataset:
|
204 |
+
if user["threshold"] >= threshold:
|
205 |
+
|
206 |
+
user_id = str(user["username"])
|
207 |
+
if block_user(user_id, user, "Violent - old dataset"):
|
208 |
+
num_users_blocked = num_users_blocked + 1
|
209 |
+
else:
|
210 |
+
# modern dataset being used
|
211 |
+
if "Violent" in dataset:
|
212 |
+
if user["violence-threshold"] >= threshold:
|
213 |
+
user_id = str(user["username"])
|
214 |
+
if block_user(user_id, user, "Violent"):
|
215 |
+
num_users_blocked = num_users_blocked + 1
|
216 |
+
continue
|
217 |
+
if "Hate Speech" in dataset:
|
218 |
+
if user["toxicity-threshold"] >= threshold:
|
219 |
+
user_id = str(user["username"])
|
220 |
+
if block_user(user_id, user, "Hate Speech"):
|
221 |
+
num_users_blocked = num_users_blocked + 1
|
222 |
+
continue
|
223 |
+
|
224 |
|
225 |
return num_users_blocked
|
226 |
|
227 |
|
228 |
def chat(selected_option=None, radio_score=None, url_params=None):
|
229 |
'''
|
230 |
+
This function is used to initialise blocking users once the user has authenticated with Twitter.
|
231 |
:param selected_option:
|
232 |
:param radio_score:
|
233 |
:param url_params:
|
|
|
239 |
history = []
|
240 |
|
241 |
# app id
|
242 |
+
if "oauth_verifier" in url_params and "oauth_token" in url_params and client is None:
|
243 |
+
client = get_client_from_tokens(url_params["oauth_verifier"], url_params["oauth_token"])
|
244 |
if radio_score != None and selected_option != None:
|
245 |
|
246 |
if client != None:
|
|
|
256 |
|
257 |
# Display to user, set options
|
258 |
history.append(
|
259 |
+
["Model tuned to a '{}%' threshold and is using the {} dataset.".format(radio_score, block_type.capitalize()),
|
|
|
260 |
"{} Account blocking initialised".format(block_type.capitalize())])
|
261 |
num_users_blocked = block_users(client, radio_score, selected_option)
|
262 |
history.append(
|
|
|
291 |
|
292 |
def update_target_website():
|
293 |
'''
|
294 |
+
Updates the URL used to authenticate WatchTower with Twitter.
|
295 |
#TODO this function is full of old code and can be optimised.
|
296 |
:return:
|
297 |
'''
|
|
|
305 |
name = "no username"
|
306 |
|
307 |
chat_history = [
|
308 |
+
["Welcome to Watchtower.".format(name), "Log in via Twitter and configure your blocking options above."]]
|
309 |
|
310 |
chatbot.value = chat_history
|
311 |
chatbot.update(value=chat_history)
|
312 |
|
313 |
+
twitter_auth_button.value = '<a href={}><img src="https://cdn.cms-twdigitalassets.com/content/dam/developer-twitter/auth-docs/sign-in-with-twitter-gray.png.twimg.1920.png" alt="Log In With Twitter"></a><br>'.format(
|
314 |
get_target_website())
|
315 |
+
twitter_auth_button.update(
|
316 |
+
value='<a href={}><img src="https://cdn.cms-twdigitalassets.com/content/dam/developer-twitter/auth-docs/sign-in-with-twitter-gray.png.twimg.1920.png" alt="Log In With Twitter"></a><br>'.format(
|
317 |
get_target_website()))
|
318 |
|
319 |
+
return '<a href={}><img src="https://cdn.cms-twdigitalassets.com/content/dam/developer-twitter/auth-docs/sign-in-with-twitter-gray.png.twimg.1920.png" alt="Log In With Twitter"></a><br>'.format(
|
320 |
+
get_target_website())
|
321 |
|
322 |
|
323 |
# The below is a JS blob used to retrieve the URL params.
|
|
|
330 |
return [text_input, url_params];
|
331 |
}
|
332 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
333 |
|
334 |
def get_chatbot_text():
|
335 |
+
return [('Welcome to Watchtower.', 'Log in via Twitter and configure your blocking options above.')]
|
336 |
|
337 |
+
def get_target_website():
|
|
|
338 |
'''
|
339 |
+
A wrapper function used for retrieving the URL a user will use to authenticate WatchTower with Twitter.
|
340 |
+
:return:
|
341 |
'''
|
342 |
+
oauth1_user_handler = tweepy.OAuth1UserHandler(
|
343 |
+
consumer_token, consumer_secret,
|
344 |
+
callback="https://hf.space/embed/User1342/WatchTower/"
|
345 |
+
)
|
346 |
+
target_website = oauth1_user_handler.get_authorization_url(signin_with_twitter=True)
|
347 |
|
348 |
+
return target_website
|
|
|
349 |
|
350 |
|
351 |
+
# The Gradio HTML component used for the 'sign in with Twitter' button
|
352 |
|
353 |
# The main chunk of code that uses Gradio blocks to create the UI
|
354 |
html_button = None
|
|
|
355 |
with block:
|
356 |
+
|
357 |
+
|
358 |
gr.HTML('''
|
359 |
<meta name="viewport" content="width=device-width, initial-scale=1">
|
360 |
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
|
|
|
362 |
|
363 |
# todo check if user signed in
|
364 |
|
365 |
+
user_message = "Log in via Twitter and configure your blocking options above."
|
366 |
+
chat_history.append(["Welcome to Watchtower.", user_message])
|
367 |
gr.HTML(value=html_data)
|
368 |
with gr.Group():
|
369 |
with gr.Row().style(equal_height=True):
|
370 |
with gr.Box():
|
371 |
+
#gr.Label(value="WatchTower", visible=True, interactive=False)
|
372 |
url_params = gr.JSON({}, visible=False, label="URL Params").style(
|
373 |
)
|
374 |
text_input = gr.Text(label="Input", visible=False).style()
|
375 |
text_output = gr.Text(label="Output", visible=False).style()
|
376 |
+
html_button = twitter_auth_button = gr.HTML(
|
377 |
+
value='<a href={}><img src="https://cdn.cms-twdigitalassets.com/content/dam/developer-twitter/auth-docs/sign-in-with-twitter-gray.png.twimg.1920.png" alt="Log In With Twitter"></a><br>'.format(
|
378 |
+
get_target_website())).style(
|
379 |
+
)
|
|
|
|
|
380 |
with gr.Row().style(equal_height=True):
|
381 |
+
radio = gr.CheckboxGroup(value=["Violent", "Hate Speech"], choices=["Violent", "Hate Speech", "Misinformation"],
|
382 |
+
interactive=False, label="Behaviour To Block").style()
|
|
|
383 |
|
384 |
+
slider = gr.Slider(value=30, interactive=True, label="Threshold Confidence Tolerance")
|
385 |
+
|
386 |
+
chatbot = gr.Chatbot(label="Watchtower Output", value=get_chatbot_text()).style(color_map=["blue","grey"])
|
387 |
|
|
|
|
|
|
|
388 |
|
389 |
btn = gr.Button("Run WatchTower").style(full_width=True).style()
|
|
|
390 |
btn.click(fn=button_pressed, inputs=[slider, url_params],
|
391 |
outputs=[text_output, chatbot], _js=get_window_url_params)
|
392 |
gr.Markdown(
|
393 |
"""___
|
394 |
<p style='text-align: center'>
|
395 |
+
Created by <a href="https://twitter.com/_JamesStevenson" target="_blank"</a> James Stevenson
|
396 |
<br/>
|
397 |
</p>"""
|
398 |
)
|
399 |
|
400 |
+
# Setup callback for when page loads (used to set a new Twitter auth target webspage)
|
401 |
block.__enter__()
|
402 |
block.set_event_trigger(
|
403 |
event_name="load", fn=update_target_website, inputs=None, outputs=[html_button], no_target=True
|
404 |
)
|
|
|
405 |
block.set_event_trigger(
|
406 |
event_name="load", fn=get_chatbot_text, inputs=None, outputs=[chatbot], no_target=True
|
407 |
)
|
408 |
|
409 |
+
#block.attach_load_events()
|
410 |
|
411 |
# Launcg the page
|
412 |
+
block.launch(enable_queue = True)
|