Portal_World.run_serv.run()   F
last analyzed

Complexity

Conditions 29

Size

Total Lines 93
Code Lines 79

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 29
eloc 79
nop 1
dl 0
loc 93
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like Portal_World.run_serv.run() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
try:
2
    import http.server
3
    from twitchio.ext import commands
4
    from twitchio.errors import AuthenticationError,EchoMessageWarning
5
    import json
6
    from re import search,findall,IGNORECASE,MULTILINE 
7
    from sys import argv
8
    import os
9
    import threading
10
    import time
11
    import ctypes
12
    import steam
13
    import socketserver
14
    import webbrowser
15
except ImportError as e:
16
    input(str(e))
17
    exit()
18
19
class Bot(commands.Bot):
20
21
    def __init__(self):
22
        with open(path3 + '/def.txt','r') as infile:
23
            default = infile.read()
24
        with open(path3 + '/settings.txt', 'r') as infile:
25
            settings = infile.read()
26
            mat = findall(r'"(.+?)"', settings)
27
            if len(mat) < 6 or default == settings:
28
                print('Error, please fill settings.txt completely in first.')
29
                exit()
30
        self.user_count = mat[3]
31
        self.settings = mat
32
        try:
33
            super().__init__(irc_token=mat[0],prefix=mat[5], nick=mat[1], initial_channels=[mat[2]])
34
        except AuthenticationError:
35
            print('settings.txt not filled in correctly. Please close this window and check you settings.')
36
37
    def close(self):
38
        os.system('exit')
39
     
40
    # Events don't need decorators when subclassed
41
    async def event_ready(self):
42
        print(f'Ready | {self.nick}')
43
44
    async def send_message(self,message,channel):
45
        if len(message) == 0 or message == None:
46
            return
47
        if len(message) < 500:
48
            await channel.send(message)
49
            return
50
51
        while len(message) >= 500:
52
            await channel.send(message[:499])
53
            message = message[499:]
54
        if len(message) != 0:
55
            await channel.send(message)
56
57
    async def event_message(self, message):
58
        try:
59
            if message.author == self.nick:
60
                return
61
62
            await self.handle_commands(message)
63
64
            await self.help_command(message)
65
66
        except Exception as e:
67
            if isinstance(e,commands.errors.CommandNotFound) or isinstance(e,EchoMessageWarning):
68
                return
69
            await message.channel.send("Uncaught error: {}".format(e))
70
71
    async def event_command_error(self, ctx, error):
72
        if isinstance(error, commands.errors.CommandNotFound) or isinstance(error,EchoMessageWarning):
73
            return
74
        raise error
75
76
    async def help_command(self,ctx):
77
        if len(self.settings) >= 8:
78
            if ctx.content.startswith(str(self.settings[5]) + str(self.settings[7])):
79
                await self.send_message('{0}add[submit] (level url), {0}remove[delete] (level name, url or id), {0}list[queue,q], {0}mylist[myqueue,myq], {0}current[np]'.format(self.settings[5]), ctx.channel)
80
81
    # Commands use a different decorator
82
    @commands.command(name='add',aliases=['submit'])
83
    async def add(self, ctx):
84
        with open(path,'r') as infile:
85
            data = json.load(infile)
86
        try:
87
            if data[len(data) - 1] == False:
88
                await self.send_message("Couldn't add level due to the queue being locked",ctx)
89
                return
90
        except IndexError:
91
            pass
92
93
        mat = search(r"""(?:(?:https?|ftp|file):\/\/|www\.|ftp\.)(?:\([-A-Z0-9+&@#\/%=~_|$?!:,.]*\)|[-A-Z0-9+&@#\/%=~_|$?!:,.])*(?:\([-A-Z0-9+&@#\/%=~_|$?!:,.]*\)|[A-Z0-9+&@#\/%=~_|$])\?id=(\d*)""", ctx.content, IGNORECASE)
94
        if mat:
95
            levelLink = mat.string
96
            authId = mat.group(1)
97
98
            levelJson = steam.webapi.post(interface='ISteamRemoteStorage',method='GetPublishedFileDetails',params={'itemcount': 1, 'publishedfileids[0]':authId})
99
            
100
            if levelJson:
101
                levelName = levelJson['response']['publishedfiledetails'][0]['title']
102
                sId = levelJson['response']['publishedfiledetails'][0]['creator']
103
                
104
                try:
105
                    if int(levelJson['response']['publishedfiledetails'][0]['consumer_app_id']) != int(self.settings[6]):
106
                        await self.send_message("Couldn't add item due to it not being for the correct game",ctx)
107
                        return
108
                except ValueError:
109
                    pass
110
111
                uinfo = steam.webapi.get(interface='ISteamUser',method='GetPlayerSummaries',version=2,params={'key':'ABCE07D6D3E64ECBB4733E9E3DA30892','steamids':sId})
112
                if uinfo:
113
                    authorName = uinfo['response']['players'][0]['personaname']
114
115
                    i = 0
116
                    for level in data:
117
                        if len(data) > 2:
118
                            if level['twitchID'] == ctx.author.id:
119
                                i += 1
120
                        if level['link'].lower() == levelLink.lower():
121
                            await self.send_message('Unable to add level due to {0} already being in the queue!'.format(levelName),ctx)
122
                            return
123
                
124
                    if len(data) > int(self.user_count) - 1:
125
                        if i >= int(self.user_count):
126
                            await self.send_message('Unable to add level due to {0} already having {1} levels in the queue!'.format(ctx.author.display_name,self.user_count),ctx)
127
                            return
128
129
                    with open(path,'w') as outfile:
130
                        data.append({'link':levelLink,'levelName':levelName,'twitchID':ctx.author.id,'levelMakerName':authorName,'submitterName':ctx.author.display_name})
131
                        json.dump(data,outfile)
132
                    await self.send_message(f'Succesfully added {levelName} to the queue at place {len(data)}!',ctx)
133
                else:
134
                    await ctx.send('Error with steam api')
135
            else:
136
                await ctx.send('Error finding level, is the url correct?')
137
        else:
138
            mat = findall(f"{self.settings[5]}([\\w]+)\\s",ctx.content)
139
            if mat:
140
                await ctx.send(f'Invalid syntax, {self.settings[5]}{mat[0]} [level url]')
141
            else:
142
                await ctx.send('Invalid syntax')
143
            
144
    @commands.command(name='remove', aliases=['delete'])
145
    async def remove(self, ctx):
146
        mat = findall(r"""(?:(?:https?|ftp|file):\/\/|www\.|ftp\.)(?:\([-A-Z0-9+&@#\/%=~_|$?!:,.]*\)|[-A-Z0-9+&@#\/%=~_|$?!:,.])*(?:\([-A-Z0-9+&@#\/%=~_|$?!:,.]*\)|[A-Z0-9+&@#\/%=~_|$])""", ctx.content, IGNORECASE)
147
        if mat:
148
            with open(path, 'r') as infile:
149
                levels = json.load(infile)
150
            i = 0
151
            for level in levels:
152 View Code Duplication
                if level['link'].lower() == mat[0].lower():
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
153
                    if level['twitchID'] == ctx.author.id or ctx.author.is_mod:
154
                        if i == 0:
155
                            await ctx.send('Cannot remove level due to it currently being played.')
156
                        else:
157
                            lname = level['levelName']
158
                            levels.pop(i)
159
                            with open(path, 'w') as outfile:
160
                                json.dump(levels, outfile)
161
162
                            await self.send_message(f'Succesfully removed {lname} from list!', ctx)
163
                    else:
164
                        await self.send_message('Cannot remove level because {0} submitted it, not {1}.'.format(level['submitterName'], ctx.author.display_name), ctx)
165
                    break
166
                i += 1
167
168
        else:
169
            mat = search(r"\s(\d)", ctx.content)
170
            if mat:
171
                with open(path, 'r') as infile:
172
                    levels = json.load(infile)
173
                    i = int(mat.group(1)) - 1
174
                    if i == 0:
175
                        await ctx.send('Cannot remove level due to it currently being played.')
176
                    elif i + 1 > len(levels):
177
                        await ctx.send('Invalid level id')
178
                    else:
179
                        if level['twitchID'] == ctx.author.id or ctx.author.is_mod:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable level does not seem to be defined.
Loading history...
180
                            lname = level['levelName']
181
                            levels.pop(i)
182
                            with open(path, 'w') as outfile:
183
                                json.dump(levels, outfile)
184
185
                            await self.send_message(f'Succesfully removed {lname} from list!', ctx)
186
                        else:
187
                            await self.send_message('Cannot remove level because {0} submitted it, not {1}.'.format(level['submitterName'], ctx.author.display_name), ctx)
188
            else:
189
                mat = search(r"\s(.+)", ctx.content)
190
                if mat:
191
                    with open(path, 'r') as infile:
192
                        levels = json.load(infile)
193
                    i = 0
194
                    for level in levels:
195 View Code Duplication
                        if level['levelName'].lower() == mat.group(1).lower():
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
196
                            if level['twitchID'] == ctx.author.id or ctx.author.is_mod:
197
                                if i == 0:
198
                                    await ctx.send('Cannot remove level due to it currently being played.')
199
                                else:
200
                                    lname = level['levelName']
201
                                    levels.pop(i)
202
                                    with open(path, 'w') as outfile:
203
                                        json.dump(levels, outfile)
204
                                    await self.send_message(f'Succesfully removed {lname} from list!', ctx)
205
                            else:
206
                                await self.send_message('Cannot remove level because {0} submitted it, not {1}.'.format(level['submitterName'], ctx.author.display_name), ctx)
207
                            break
208
                        i += 1
209
                else:
210
                    mat = findall(f"{self.settings[5]}([\\w]+)\\s", ctx.content)
211
                    if mat:
212
                        await ctx.send(f'Invalid syntax, {self.settings[5]}{mat[0]} [link, level name or level id]')
213
                    else:
214
                        await ctx.send('You should not be seeing this, something went terribly wrong. Anyways, incorrect syntax.')
215
    
216
    @commands.command(name='promote')
217
    async def promote(self,ctx):
218
        mat = findall(r"""(?:(?:https?|ftp|file):\/\/|www\.|ftp\.)(?:\([-A-Z0-9+&@#\/%=~_|$?!:,.]*\)|[-A-Z0-9+&@#\/%=~_|$?!:,.])*(?:\([-A-Z0-9+&@#\/%=~_|$?!:,.]*\)|[A-Z0-9+&@#\/%=~_|$])""", ctx.content, IGNORECASE)
219
        if mat:
220
            with open(path, 'r') as infile:
221
                levels = json.load(infile)
222
            i = 0
223
            for level in levels:
224
                if level['link'].lower() == mat[0].lower():
225
                    if ctx.author.is_mod:
226
                        if i == 0:
227
                            await ctx.send('Cannot promote level due to it currently being played.')
228
                        else:
229
                            lname = level['levelName']
230
                            levels.pop(i)
231
                            levels.insert(1, level)
232
                            with open(path, 'w') as outfile:
233
                                json.dump(levels, outfile)
234
235
                            await self.send_message(f'{lname} is now up next!', ctx)
236
                    break
237
                i += 1
238
        else:
239
            mat = search(r"\s(\d)", ctx.content)
240
            if mat:
241
                with open(path, 'r') as infile:
242
                    levels = json.load(infile)
243
                    i = int(mat.group(1)) - 1
244
                    if ctx.author.is_mod:
245
                        if i == 0:
246
                            await ctx.send('Cannot promote level due to it currently being played.')
247
                        elif i + 1 > len(levels):
248
                            await ctx.send('Invalid level id')
249
                        else:
250
                            level = levels[i]
251
                            lname = level['levelName']
252
                            levels.pop(i)
253
                            levels.insert(1, level)
254
                            with open(path, 'w') as outfile:
255
                                json.dump(levels, outfile)
256
                            await self.send_message(f'{lname} is now up next!', ctx)
257
            else:
258
                mat = search(r"\s(.+)", ctx.content)
259
                if mat:
260
                    with open(path, 'r') as infile:
261
                        levels = json.load(infile)
262
                    i = 0
263
                    for level in levels:
264
                        if level['levelName'].lower() == mat.group(1).lower():
265
                            if ctx.author.is_mod:
266
                                if i == 0:
267
                                    await ctx.send('Cannot promote level due to it currently being played.')
268
                                else:
269
                                    lname = level['levelName']
270
                                    levels.pop(i)
271
                                    levels.insert(1, level)
272
                                    with open(path, 'w') as outfile:
273
                                        json.dump(levels, outfile)
274
275
                                    await self.send_message(f'{lname} is now up next!', ctx)
276
                            break
277
                        i += 1
278
                else:
279
                    self.send_message(f'Invalid syntax: {self.settings[5]}promote [level name, url or id]',ctx)
280
281
    @commands.command(name='list',aliases=['queue','q'])
282
    async def list(self,ctx):
283
        with open(path,'r') as infile:
284
            levels = json.load(infile)
285
        out = ""
286
        i = 1
287
        if len(levels) > 0:
288
            if levels[len(levels) - 1] == False:
289
                levels = levels[:len(levels) - 1]
290
            if len(levels) <= int(self.settings[4]):
291
                for level in levels:
292
                    out += "{3} - Level: '{0}' - Made by: '{1}' - Submitted by: '{2}' ".format(level['levelName'],level['levelMakerName'],level['submitterName'],i)
293
                    i += 1
294
                await self.send_message(out,ctx)
295
            else:
296
                for level in levels:
297
                    if i <= int(self.settings[4]):
298
                        out += "{3} - Level: '{0}' - Made by: '{1}' - Submitted by: '{2}' ".format(level['levelName'],level['levelMakerName'],level['submitterName'],i)
299
                    i += 1
300
                if i > int(self.settings[4]):
301
                    ileft = i - int(self.settings[4])
302
                    out += f" And {ileft} more levels in queue."
303
                await self.send_message(out,ctx)
304
        else:
305
            await ctx.send('The queue is currently empty.')
306
307
    @commands.command(name='mylist',aliases=['myqueue','myq'])
308
    async def mylist(self,ctx):
309
        with open(path,'r') as infile:
310
            levels = json.load(infile)
311
        out = ""
312
        lInQueue = False
313
        i = 1
314
        if len(levels) > 0:
315
            if levels[len(levels) - 1] == False:
316
                levels = levels[:len(levels) - 1]
317
            if len(levels) <= int(self.settings[4]):
318
                for level in levels:
319
                    if level['twitchID'] == ctx.author.id:
320
                        out += "{3} - Level: '{0}' - Made by: '{1}' - Submitted by: '{2}' ".format(level['levelName'],level['levelMakerName'],level['submitterName'],i)
321
                        lInQueue = True
322
                    i += 1
323
                if out != "" and lInQueue:
324
                    await self.send_message(out,ctx)
325
                else:
326
                    await self.send_message('{} has no levels in the queue.'.format(ctx.author.display_name),ctx)
327
            else:
328
                for level in levels:
329
                    if i <= int(self.settings[4]):
330
                        if level['twitchID'] == ctx.author.id:
331
                            out += "{3} - Level: '{0}' - Made by: '{1}' - Submitted by: '{2}' ".format(level['levelName'],level['levelMakerName'],level['submitterName'],i)
332
                            lInQueue = True
333
                    else:
334
                        ileft = i - int(self.settings[4])
335
                        out += f" And {ileft} more levels in queue."
336
                        break
337
                    i += 1
338
                if out != "" and lInQueue:
339
                    await self.send_message(out,ctx)
340
                else:
341
                    await self.send_message('{} has no levels in the queue.'.format(ctx.author.display_name),ctx)
342
        else:
343
            await ctx.send('The queue is currently empty.')
344
345
    @commands.command(name='lock')
346
    async def lock(self, ctx):
347
        if ctx.author.is_mod:
348
            with open(path, 'r') as infile:
349
                levels = json.load(infile)
350
            if len(levels) > 0:
351
                if levels[len(levels) - 1] == False:
352
                    await self.send_message('Queue was already locked', ctx)
353
                    return
354
                else:
355
                    levels.append(False)
356
                    with open(path, 'w') as outfile:
357
                        json.dump(levels, outfile)
358
                    await self.send_message('Succesfully locked queue', ctx)
359
            else:
360
                levels.append(False)
361
                with open(path, 'w') as outfile:
362
                    json.dump(levels, outfile)
363
                await self.send_message('Succesfully locked queue', ctx)
364
365
    @commands.command(name='unlock')
366
    async def unlock(self, ctx):
367
        if ctx.author.is_mod:
368
            with open(path, 'r') as infile:
369
                levels = json.load(infile)
370
            if len(levels) > 0:
371
                if levels[len(levels) - 1] == False:
372
                    levels.pop(len(levels) - 1)
373
                    with open(path, 'w') as outfile:
374
                        json.dump(levels, outfile)
375
                    await self.send_message('Succesfully unlocked queue', ctx)
376
                else:
377
                    await self.send_message('Queue was already unlocked', ctx)
378
            else:
379
                await self.send_message('Queue was already unlocked', ctx)
380
381
    @commands.command(name='next', aliases=['skip'])
382
    async def next(self, ctx):
383
        if ctx.author.is_mod:
384
            with open(path, 'r') as infile:
385
                levels = json.load(infile)
386
            if len(levels) > 0:
387
                if levels[0] != False:
388
                    levels.pop(0)
389
                    with open(path, 'w') as outfile:
390
                        json.dump(levels, outfile)
391
                    await self.send_message('Succesfully skipped level', ctx)
392
                    return
393
            await self.send_message("Couldn't skip level due to the queue being empty", ctx)
394
395
    @commands.command(name='clear',aliases=['reset','empty','init'])
396
    async def clear(self,ctx):
397
        if ctx.author.is_mod:
398
            with open(path,'w') as outfile:
399
                outfile.write('[]')
400
            await ctx.send('Succesfully cleared queue!')
401
402
    @commands.command(name='current',aliases=['currentlevel','np','nowplaying','now'])
403
    async def current(self,ctx):
404
        with open(path,'r') as infile:
405
            levels = json.load(infile)
406
        if len(levels) > 0 and levels[0] != False:
407
            await self.send_message('The current level is "{0}" by "{1}" submitted by "{2}" link {3}.'.format(levels[0]['levelName'],levels[0]['levelMakerName'],levels[0]['submitterName'],levels[0]['link']),ctx)
408
        else:
409
            await ctx.send('The queue is currently empty.')
410
411
412
if len(argv) < 2:
413
    path = os.path.dirname(os.path.abspath(__file__)) + "/dir/levels.json"
414
else:
415
    path = argv[1]
416
417
if len(argv) < 3:
418
    path3 = os.path.dirname(os.path.abspath(__file__)) + '/dir'
419
else:
420
    path3 = argv[2]
421
422
favicon = os.path.dirname(os.path.abspath(__file__)) + '/imgs/favicon.ico'
423
424
with open(path3 + '/def.txt', 'r') as infile:
425
    default = infile.read()
426
427
with open(path3 + '/settings.txt', 'r') as infile:
428
    settings = infile.read()
429
    mat = findall(r'"(.+?)"', settings)
430
    if len(mat) < 7 or default == settings:
431
        input('Error, please fill settings.txt completely in first. ')
432
        exit()
433
434
class run_bot(threading.Thread):
435
    def __init__(self, name):
436
        threading.Thread.__init__(self)
437
        self.name = name
438
        self.bot = Bot()
439
440
    def run(self):
441
        try:
442
            self.bot.run()
443
        finally:
444
            exit()
445
446
    def stop(self):
447
        self.bot.close()
448
449
class run_serv(threading.Thread):
450
    def __init__(self, name):
451
        threading.Thread.__init__(self)
452
        self.name = name
453
        
454
    def run(self):
455
456
        class getHandler(http.server.SimpleHTTPRequestHandler):
457
            
458
            def do_GET(self):
459
                try:
460
                    http.server.SimpleHTTPRequestHandler.do_GET(self)
461
                except ConnectionAbortedError:
462
                    pass
463
                except FileNotFoundError:
464
                    pass
465
                try:
466
                    mat = findall(r"/dashboard.html\?removeLevelId=([\w]+)", self.path)
467
                    if mat:
468
                        try:
469
                            removeIndex = int(mat[0])
470
                        except ValueError:
471
                            if mat[0] == 'all':
472
                                with open(path,'w') as outfile:
473
                                    outfile.write('[]')
474
                            elif mat[0] == 'lock':
475
                                with open(path, 'r') as infile:
476
                                    levels = json.loads(infile.read())
477
                                    try:
478
                                        if levels[len(levels) - 1] == False:
479
                                            levels.pop(len(levels) - 1)
480
                                        else:
481
                                            levels.append(False)
482
                                    except IndexError:
483
                                        levels.append(False)
484
                                    with open(path, 'w') as outfile:
485
                                        json.dump(levels, outfile)
486
                        else:
487
                            with open(path,'r') as infile:
488
                                levels = json.load(infile)
489
                            if levels[removeIndex] != False:
490
                                levels.pop(removeIndex)
491
                            with open(path,'w') as outfile:
492
                                json.dump(levels,outfile)
493
                    else:
494
                        mat = findall(r"/dashboard.html\?promoteLevelId=([\w]+)", self.path)
495
                        if mat:
496
                            with open(path, 'r') as infile:
497
                                levels = json.load(infile)
498
                                i = int(mat[0]) - 1
499
                                if i != 0 or i + 1 < len(levels):
500
                                    level = levels[i]
501
                                    levels.pop(i)
502
                                    levels.insert(1, level)
503
                                    with open(path, 'w') as outfile:
504
                                        json.dump(levels, outfile)
505
506
                        mat = findall(r"\/dashboard\.html\?levelName=(.*?)&levelMakerName=(.*?)&submitterName=(.*?)&link=(.*?)&d=", self.path)
507
                        if mat:
508
                            link = mat[0][3]
509
                            levelName = mat[0][0].replace('+',' ')
510
                            levelMakerName = mat[0][1].replace('+', ' ')
511
                            submitterName = mat[0][2].replace('+', ' ')
512
                            
513
                            l = {'link':link,'levelName':levelName,'twitchID':None,'levelMakerName':levelMakerName,'submitterName':submitterName}
514
                            with open(path,'r') as infile:
515
                                levels = json.load(infile)
516
                            levels.append(l)
517
                            with open(path,'w') as outfile:
518
                                json.dump(levels,outfile)
519
520
                except Exception:
521
                    pass
522
523
            def do_HEAD(self):
524
                http.server.SimpleHTTPRequestHandler.do_HEAD(self)
525
526
            def log_message(self, format, *args):
527
                return
528
529
            def log_error(self, format, *args):
530
                return
531
532
        try:
533
            Handler = getHandler
534
            PORT = 8000
535
            os.chdir(str(path3))
536
            with socketserver.TCPServer(("", PORT), Handler) as httpd:
537
                httpd.serve_forever()
538
        finally:
539
            while True:
540
                try:
541
                    httpd.shutdown()
0 ignored issues
show
introduced by
The variable httpd does not seem to be defined for all execution paths.
Loading history...
542
                except Exception:
543
                    pass
544
                else:
545
                    break
546
            exit()
547
548
    def get_id(self):
549
        # returns id of the respective thread
550
        for id, thread in threading._active.items():
551
            if thread is self:
552
                return id
553
554
    def stop(self):
555
        thread_id = self.get_id()
556
        res = ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id,ctypes.py_object(SystemExit))
557
        if res > 1:
558
            ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0)
559
560
if __name__ == '__main__':
561
562
    rbot = run_bot('twitch')
563
    rserv = run_serv('localhost')
564
    rbot.start()
565
    rserv.start()
566
    time.sleep(1)
567
    webbrowser.open('http://localhost:8000/dashboard.html')
568