User1342 commited on
Commit
099b590
·
1 Parent(s): 11a9112

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +400 -0
app.py ADDED
@@ -0,0 +1,400 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ from pprint import pprint
4
+ from mastodon import Mastodon #Mastodon.py
5
+
6
+
7
+ def client_log_in():
8
+
9
+ secret_file = open("w", "ivory-secret.txt")
10
+ secret_file.write("{}\n".format(os.getenv('app_secret_1'))
11
+ secret_file.write("{}\n".format(os.getenv('app_secret_2'))
12
+ secret_file.write("{}\n".format(os.getenv('https://infosec.exchange'))
13
+ secret_file.write("{}\n".format(os.getenv('WatchTower | Ivory'))
14
+ secret_file.close()
15
+
16
+ mastodon = Mastodon(
17
+ client_id="ivory-secret.txt",
18
+ api_base_url='https://infosec.exchange'
19
+ )
20
+
21
+ mastodon.log_in(
22
+ os.getenv('username'),
23
+ os.getenv('password'),
24
+ scopes=["write:blocks","write:mutes"],
25
+ redirect_uri="https://user1342-ivory.hf.space/",
26
+ )
27
+
28
+ return mastodon
29
+
30
+ def get_auth_url(mastodon):
31
+
32
+ return mastodon.auth_request_url(client_id="irrelevant-secret.txt", scopes=["write:blocks","write:mutes"], redirect_uris="https://user1342-ivory.hf.space/")
33
+
34
+ def login_from_code(code):
35
+ mastodon = Mastodon(
36
+ client_id="irrelevant-secret.txt",
37
+ api_base_url='https://infosec.exchange'
38
+ )
39
+
40
+ mastodon.log_in(code=code,
41
+ scopes=["write:blocks","write:mutes"],
42
+ redirect_uri="https://user1342-ivory.hf.space/"
43
+
44
+ )
45
+
46
+ return mastodon
47
+
48
+ def get_posts_from_user(masterdom, user_id):
49
+ return masterdom.account_statuses(id=user_id)
50
+
51
+
52
+ #!/usr/bin/env python
53
+ # coding: utf-8
54
+ import json
55
+ import os
56
+ import re
57
+ import time
58
+ from random import random
59
+ import socket
60
+
61
+ from threading import Thread
62
+ from time import sleep
63
+
64
+ html_data = '''
65
+ <!DOCTYPE html>
66
+ <html>
67
+ <head>
68
+ <meta charset="UTF-8">
69
+ <meta name="viewport" content="width=device-width, initial-scale=1">
70
+ <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
71
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Poppins">
72
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
73
+ <style>
74
+ body,h1,h2,h3,h4,h5 {font-family: "Poppins", sans-serif}
75
+ body {font-size: 16px;}
76
+ img {margin-bottom: -8px;}
77
+ .mySlides {display: none;}
78
+ </style>
79
+ </head>
80
+ <body class="w3-content w3-black" style="max-width:1500px;">
81
+
82
+ <!-- The App Section -->
83
+
84
+ <div class="w3-padding-large w3-white">
85
+ <div class="w3-row-padding-large">
86
+ <div class="w3-col">
87
+ <h1 class="w3-jumbo"><b>WatchTower 🐦🚧</b></h1>
88
+ <h1 class="w3-xxxlarge w3-text-blue"><b>Remove Unfavorable Tweets From Your Feed </b></h1>
89
+ <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 regullary to ensure you're up to date! Depending on how many users WatchTower is blocking/ muting it <b>can take upwards of 15 minutes to complete</b> - just leave this page open, grab a cup of tea, and come back in a few minutes. 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 botton of the page and click run!</p>
90
+ <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'">
91
+ <i class=""></i> Find Out More! 💬
92
+ </button></a>
93
+ <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'">
94
+ <i class=""></i> Support The Creator! ❤
95
+ </button></a>
96
+ <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'">
97
+ <i class=""></i> Follow Us! 🐦
98
+ </button></a>
99
+ </div>
100
+ </div>
101
+ </div>
102
+
103
+ <!-- Modal -->
104
+ <div id="download" class="w3-modal w3-animate-opacity">
105
+ <div class="w3-modal-content" style="padding:32px">
106
+ <div class="w3-container w3-white">
107
+ <i onclick="document.getElementById('download').style.display='none'" class="fa fa-remove w3-xlarge w3-button w3-transparent w3-right w3-xlarge"></i>
108
+ <h2 class="w3-wide">DOWNLOAD</h2>
109
+ <p>Download the app in AppStore, Google Play or Microsoft Store.</p>
110
+ <i class="fa fa-android w3-large"></i> <i class="fa fa-apple w3-large"></i> <i class="fa fa-windows w3-large"></i>
111
+ <p><input class="w3-input w3-border" type="text" placeholder="Enter e-mail"></p>
112
+ <button type="button" class="w3-button w3-block w3-padding-large w3-red w3-margin-bottom" onclick="document.getElementById('download').style.display='none'">Fake Download</button>
113
+ </div>
114
+ </div>
115
+ </div>
116
+
117
+ <script>
118
+ // Slideshow
119
+ var slideIndex = 1;
120
+ showDivs(slideIndex);
121
+
122
+ function plusDivs(n) {
123
+ showDivs(slideIndex += n);
124
+ }
125
+
126
+ function showDivs(n) {
127
+ var i;
128
+ var x = document.getElementsByClassName("mySlides");
129
+ if (n > x.length) {slideIndex = 1}
130
+ if (n < 1) {slideIndex = x.length}
131
+ for (i = 0; i < x.length; i++) {
132
+ x[i].style.display = "none";
133
+ }
134
+ x[slideIndex-1].style.display = "block";
135
+ }
136
+ </script>
137
+ <br>
138
+ <br>
139
+ <br>
140
+ </body>
141
+ </html>
142
+
143
+ '''
144
+
145
+ # Imports
146
+ import json
147
+ import os
148
+ import time
149
+ import gradio as gr
150
+
151
+ # Setup the gradio block and add some generic CSS
152
+ 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")
153
+
154
+ # Chat history variable used for the chatbot prompt on the 'getting started' page.
155
+ chat_history = []
156
+
157
+
158
+ def get_client_from_tokens(code):
159
+ '''
160
+ This function is used for generating a Tweepy client object based on Oauth verifier and token paramiters
161
+ :param oauth_verifier:
162
+ :param oauth_token:
163
+ :return: A Tweepy client object
164
+ '''
165
+
166
+
167
+
168
+ return login_from_code(code)
169
+
170
+ def block_user(user_id, user, reason):
171
+ finished = False
172
+ blocked = True
173
+ while not finished:
174
+ client.account_block(user_id)
175
+
176
+
177
+ print("Blocked {}, for {}".format(user, reason))
178
+ return blocked
179
+
180
+ def block_users(client, threshold, dataset):
181
+ '''
182
+ Used for blocking a series of users based on the threshold and datasets provided. Here the users folder is used.
183
+ :param client:
184
+ :param threshold:
185
+ :param dataset:
186
+ :return: The number of blocked users.
187
+ '''
188
+ num_users_blocked = 0
189
+
190
+ for filename in os.listdir("users"):
191
+ filename = os.path.join("users", filename)
192
+ print("File {} open".format(filename))
193
+ user_file = open(filename, "r")
194
+ users = json.load(user_file)
195
+
196
+ for user in users:
197
+ print("Reviewing user {}".format(user))
198
+
199
+ if "threshold" in user:
200
+ # old type of dataset being used, only 'violent' data available
201
+ if "Violent" in dataset:
202
+ if user["threshold"] >= threshold:
203
+
204
+ user_id = str(user["username"])
205
+ if block_user(user_id, user, "Violent - old dataset"):
206
+ num_users_blocked = num_users_blocked + 1
207
+ else:
208
+ # modern dataset being used
209
+ if "Violent" in dataset:
210
+ if user["violence-threshold"] >= threshold:
211
+ user_id = str(user["username"])
212
+ if block_user(user_id, user, "Violent"):
213
+ num_users_blocked = num_users_blocked + 1
214
+ continue
215
+ if "Hate Speech" in dataset:
216
+ if user["toxicity-threshold"] >= threshold:
217
+ user_id = str(user["username"])
218
+ if block_user(user_id, user, "Hate Speech"):
219
+ num_users_blocked = num_users_blocked + 1
220
+ continue
221
+
222
+
223
+ return num_users_blocked
224
+
225
+
226
+ def chat(selected_option=None, radio_score=None, url_params=None):
227
+ '''
228
+ This function is used to initialise blocking users once the user has authenticated with Twitter.
229
+ :param selected_option:
230
+ :param radio_score:
231
+ :param url_params:
232
+ :return: the chatbot history is returned (including information on blocked accounts).
233
+ '''
234
+
235
+ global client
236
+ global chat_history
237
+ history = []
238
+
239
+ # app id
240
+ if "code" in url_params and client is None:
241
+ client = get_client_from_tokens(url_params["code"])
242
+ if radio_score != None and selected_option != None:
243
+
244
+ if client != None:
245
+
246
+ # Extract the list to a string representation
247
+ if type(selected_option) is list:
248
+ block_type = ""
249
+ for b_type in selected_option:
250
+ block_type = block_type + " + " + b_type.capitalize()
251
+ block_type = "'" + block_type[3:] + "'"
252
+ else:
253
+ block_type = selected_option
254
+
255
+ # Display to user, set options
256
+ history.append(
257
+ ["Model tuned to a '{}%' threshold and is using the {} dataset.".format(radio_score, block_type.capitalize()),
258
+ "{} Account blocking initialised".format(block_type.capitalize())])
259
+ num_users_blocked = block_users(client, radio_score, selected_option)
260
+ history.append(
261
+ ["Blocked {} user account(s).".format(num_users_blocked), "Thank you for using Watchtower."])
262
+ elif radio_score != None or selected_option != None:
263
+ chat_history.append(["Initialisation error!", "Please tune the model by using the above options"])
264
+
265
+ history = chat_history + history
266
+ chatbot.value = history
267
+ chatbot.update(value=history)
268
+ client = None
269
+ return history
270
+
271
+
272
+ def infer(prompt):
273
+ pass
274
+
275
+
276
+ have_initialised = False
277
+ client = None
278
+ name = None
279
+
280
+
281
+ def button_pressed(slider_value, url_params):
282
+ # print(url_params)
283
+ return [None, chat(radio.value, slider_value, url_params)]
284
+
285
+
286
+ # The website that the user will visit to authenticate WatchTower.
287
+ target_website = None
288
+
289
+
290
+ def update_target_website():
291
+ '''
292
+ Updates the URL used to authenticate WatchTower with Twitter.
293
+ #TODO this function is full of old code and can be optimised.
294
+ :return:
295
+ '''
296
+ global have_initialised
297
+ global chatbot
298
+ global chat_history
299
+ global client
300
+ global name
301
+
302
+ client = None
303
+ name = "no username"
304
+
305
+ chat_history = [
306
+ ["Welcome to Watchtower.".format(name), "Log in via Twitter and configure your blocking options above."]]
307
+
308
+ chatbot.value = chat_history
309
+ chatbot.update(value=chat_history)
310
+
311
+ 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(
312
+ get_target_website())
313
+ twitter_auth_button.update(
314
+ 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(
315
+ get_target_website()))
316
+
317
+ 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(
318
+ get_target_website())
319
+
320
+
321
+ # The below is a JS blob used to retrieve the URL params.
322
+ # Thanks to here: https://discuss.huggingface.co/t/hugging-face-and-gradio-url-paramiters/21110/2
323
+ get_window_url_params = """
324
+ function(text_input, url_params) {
325
+ console.log(text_input, url_params);
326
+ const params = new URLSearchParams(window.location.search);
327
+ url_params = Object.fromEntries(params);
328
+ return [text_input, url_params];
329
+ }
330
+ """
331
+
332
+
333
+ def get_target_website():
334
+ '''
335
+ A wrapper function used for retrieving the URL a user will use to authenticate WatchTower with Masterdon.
336
+ :return: auth url
337
+ '''
338
+
339
+ mastodon = client_log_in()
340
+ return get_auth_url(mastodon)
341
+
342
+
343
+ # The Gradio HTML component used for the 'sign in with Twitter' button
344
+
345
+ # The main chunk of code that uses Gradio blocks to create the UI
346
+ html_button = None
347
+ with block:
348
+
349
+
350
+ gr.HTML('''
351
+ <meta name="viewport" content="width=device-width, initial-scale=1">
352
+ <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
353
+ ''')
354
+
355
+ # todo check if user signed in
356
+
357
+ user_message = "Log in via Twitter and configure your blocking options above."
358
+ chat_history.append(["Welcome to Watchtower.", user_message])
359
+ gr.HTML(value=html_data)
360
+ with gr.Group():
361
+ with gr.Row().style(equal_height=True):
362
+ with gr.Box():
363
+ #gr.Label(value="WatchTower", visible=True, interactive=False)
364
+ url_params = gr.JSON({}, visible=False, label="URL Params").style(
365
+ )
366
+ text_input = gr.Text(label="Input", visible=False).style()
367
+ text_output = gr.Text(label="Output", visible=False).style()
368
+ html_button = twitter_auth_button = gr.HTML(
369
+ 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(
370
+ get_target_website())).style(
371
+ )
372
+ with gr.Row().style(equal_height=True):
373
+ radio = gr.CheckboxGroup(value=["Violent", "Hate Speech"], choices=["Violent", "Hate Speech", "Misinformation"],
374
+ interactive=False, label="Behaviour To Block").style()
375
+
376
+ slider = gr.Slider(value=30, interactive=True, label="Threshold Confidence Tolerance")
377
+
378
+ chatbot = gr.Chatbot(label="Watchtower Output", value=[('Welcome to Watchtower.', 'Log in via Twitter and configure your blocking options above.')]).style(color_map=["blue","grey"])
379
+
380
+ btn = gr.Button("Run WatchTower").style(full_width=True).style()
381
+ btn.click(fn=button_pressed, inputs=[slider, url_params],
382
+ outputs=[text_output, chatbot], _js=get_window_url_params)
383
+ gr.Markdown(
384
+ """___
385
+ <p style='text-align: center'>
386
+ Created by <a href="https://twitter.com/_JamesStevenson" target="_blank"</a> James Stevenson
387
+ <br/>
388
+ </p>"""
389
+ )
390
+
391
+ # Setup callback for when page loads (used to set a new Twitter auth target webspage)
392
+ block.__enter__()
393
+ block.set_event_trigger(
394
+ event_name="load", fn=update_target_website, inputs=None, outputs=[html_button], no_target=True
395
+ )
396
+
397
+ block.attach_load_events()
398
+
399
+ # Launcg the page
400
+ block.launch(enable_queue = True)