check config and little refactoring
[rainbowstream.git] / rainbowstream / rainbow.py
... / ...
CommitLineData
1"""
2Colorful user's timeline stream
3"""
4from multiprocessing import Process
5
6import os
7import os.path
8import sys
9import signal
10import argparse
11import time
12import requests
13import webbrowser
14import json
15
16from twitter.stream import TwitterStream, Timeout, HeartbeatTimeout, Hangup
17from twitter.api import *
18from twitter.oauth import OAuth, read_token_file
19from twitter.oauth_dance import oauth_dance
20from twitter.util import printNicely
21
22from .draw import *
23from .colors import *
24from .config import *
25from .consumer import *
26from .interactive import *
27from .db import *
28from .c_image import *
29from .py3patch import *
30
31
32g = {}
33db = RainbowDB()
34cmdset = [
35 'switch',
36 'trend',
37 'home',
38 'view',
39 'mentions',
40 't',
41 'rt',
42 'quote',
43 'allrt',
44 'fav',
45 'rep',
46 'del',
47 'ufav',
48 's',
49 'mes',
50 'show',
51 'open',
52 'ls',
53 'inbox',
54 'sent',
55 'trash',
56 'whois',
57 'fl',
58 'ufl',
59 'mute',
60 'unmute',
61 'muting',
62 'block',
63 'unblock',
64 'report',
65 'list',
66 'cal',
67 'theme',
68 'h',
69 'c',
70 'q'
71]
72
73
74def parse_arguments():
75 """
76 Parse the arguments
77 """
78 parser = argparse.ArgumentParser(description=__doc__ or "")
79 parser.add_argument(
80 '-to',
81 '--timeout',
82 help='Timeout for the stream (seconds).')
83 parser.add_argument(
84 '-ht',
85 '--heartbeat-timeout',
86 help='Set heartbeat timeout.',
87 default=90)
88 parser.add_argument(
89 '-nb',
90 '--no-block',
91 action='store_true',
92 help='Set stream to non-blocking.')
93 parser.add_argument(
94 '-tt',
95 '--track-keywords',
96 help='Search the stream for specific text.')
97 parser.add_argument(
98 '-fil',
99 '--filter',
100 help='Filter specific screen_name.')
101 parser.add_argument(
102 '-ig',
103 '--ignore',
104 help='Ignore specific screen_name.')
105 parser.add_argument(
106 '-iot',
107 '--image-on-term',
108 action='store_true',
109 help='Display all image on terminal.')
110 return parser.parse_args()
111
112
113def authen():
114 """
115 Authenticate with Twitter OAuth
116 """
117 # When using rainbow stream you must authorize.
118 twitter_credential = os.environ.get(
119 'HOME',
120 os.environ.get(
121 'USERPROFILE',
122 '')) + os.sep + '.rainbow_oauth'
123 if not os.path.exists(twitter_credential):
124 oauth_dance("Rainbow Stream",
125 CONSUMER_KEY,
126 CONSUMER_SECRET,
127 twitter_credential)
128 oauth_token, oauth_token_secret = read_token_file(twitter_credential)
129 return OAuth(
130 oauth_token,
131 oauth_token_secret,
132 CONSUMER_KEY,
133 CONSUMER_SECRET)
134
135
136def get_decorated_name():
137 """
138 Beginning of every line
139 """
140 t = Twitter(auth=authen())
141 name = '@' + t.account.verify_credentials()['screen_name']
142 g['original_name'] = name[1:]
143 g['decorated_name'] = color_func(c['DECORATED_NAME'])('[' + name + ']: ')
144
145 files = os.listdir(os.path.dirname(__file__) + '/colorset')
146 themes = [f.split('.')[0] for f in files if f.split('.')[-1] == 'json']
147 g['themes'] = themes
148 db.theme_store(c['THEME'])
149
150
151def switch():
152 """
153 Switch stream
154 """
155 try:
156 target = g['stuff'].split()[0]
157
158 # Filter and ignore
159 args = parse_arguments()
160 try:
161 if g['stuff'].split()[-1] == '-f':
162 only = raw_input('Only nicks: ')
163 ignore = raw_input('Ignore nicks: ')
164 args.filter = filter(None, only.split(','))
165 args.ignore = filter(None, ignore.split(','))
166 elif g['stuff'].split()[-1] == '-d':
167 args.filter = c['ONLY_LIST']
168 args.ignore = c['IGNORE_LIST']
169 except:
170 printNicely(red('Sorry, wrong format.'))
171 return
172
173 # Public stream
174 if target == 'public':
175 keyword = g['stuff'].split()[1]
176 if keyword[0] == '#':
177 keyword = keyword[1:]
178 # Kill old process
179 os.kill(g['stream_pid'], signal.SIGKILL)
180 args.track_keywords = keyword
181 # Start new process
182 p = Process(
183 target=stream,
184 args=(
185 c['PUBLIC_DOMAIN'],
186 args))
187 p.start()
188 g['stream_pid'] = p.pid
189
190 # Personal stream
191 elif target == 'mine':
192 # Kill old process
193 os.kill(g['stream_pid'], signal.SIGKILL)
194 # Start new process
195 p = Process(
196 target=stream,
197 args=(
198 c['USER_DOMAIN'],
199 args,
200 g['original_name']))
201 p.start()
202 g['stream_pid'] = p.pid
203 printNicely('')
204 if args.filter:
205 printNicely(cyan('Only: ' + str(args.filter)))
206 if args.ignore:
207 printNicely(red('Ignore: ' + str(args.ignore)))
208 printNicely('')
209 except:
210 printNicely(red('Sorry I can\'t understand.'))
211
212
213def trend():
214 """
215 Trend
216 """
217 t = Twitter(auth=authen())
218 # Get country and town
219 try:
220 country = g['stuff'].split()[0]
221 except:
222 country = ''
223 try:
224 town = g['stuff'].split()[1]
225 except:
226 town = ''
227
228 avail = t.trends.available()
229 # World wide
230 if not country:
231 trends = t.trends.place(_id=1)[0]['trends']
232 print_trends(trends)
233 else:
234 for location in avail:
235 # Search for country and Town
236 if town:
237 if location['countryCode'] == country \
238 and location['placeType']['name'] == 'Town' \
239 and location['name'] == town:
240 trends = t.trends.place(_id=location['woeid'])[0]['trends']
241 print_trends(trends)
242 # Search for country only
243 else:
244 if location['countryCode'] == country \
245 and location['placeType']['name'] == 'Country':
246 trends = t.trends.place(_id=location['woeid'])[0]['trends']
247 print_trends(trends)
248
249
250def home():
251 """
252 Home
253 """
254 t = Twitter(auth=authen())
255 num = c['HOME_TWEET_NUM']
256 if g['stuff'].isdigit():
257 num = int(g['stuff'])
258 for tweet in reversed(t.statuses.home_timeline(count=num)):
259 draw(t=tweet, iot=g['iot'])
260 printNicely('')
261
262
263def view():
264 """
265 Friend view
266 """
267 t = Twitter(auth=authen())
268 user = g['stuff'].split()[0]
269 if user[0] == '@':
270 try:
271 num = int(g['stuff'].split()[1])
272 except:
273 num = c['HOME_TWEET_NUM']
274 for tweet in reversed(t.statuses.user_timeline(count=num, screen_name=user[1:])):
275 draw(t=tweet, iot=g['iot'])
276 printNicely('')
277 else:
278 printNicely(red('A name should begin with a \'@\''))
279
280
281def mentions():
282 """
283 Mentions timeline
284 """
285 t = Twitter(auth=authen())
286 num = c['HOME_TWEET_NUM']
287 if g['stuff'].isdigit():
288 num = int(g['stuff'])
289 for tweet in reversed(t.statuses.mentions_timeline(count=num)):
290 draw(t=tweet, iot=g['iot'])
291 printNicely('')
292
293
294def tweet():
295 """
296 Tweet
297 """
298 t = Twitter(auth=authen())
299 t.statuses.update(status=g['stuff'])
300
301
302def retweet():
303 """
304 ReTweet
305 """
306 t = Twitter(auth=authen())
307 try:
308 id = int(g['stuff'].split()[0])
309 except:
310 printNicely(red('Sorry I can\'t understand.'))
311 return
312 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
313 t.statuses.retweet(id=tid, include_entities=False, trim_user=True)
314
315
316def quote():
317 """
318 Quote a tweet
319 """
320 t = Twitter(auth=authen())
321 try:
322 id = int(g['stuff'].split()[0])
323 except:
324 printNicely(red('Sorry I can\'t understand.'))
325 return
326 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
327 tweet = t.statuses.show(id=tid)
328 screen_name = tweet['user']['screen_name']
329 text = tweet['text']
330 quote = '\"@' + screen_name + ': ' + text + '\"'
331 quote = quote.encode('utf8')
332 notice = light_magenta('Compose mode ')
333 notice += light_yellow('(Enter nothing will cancel the quote)')
334 notice += light_magenta(':')
335 printNicely(notice)
336 extra = raw_input(quote)
337 if extra:
338 t.statuses.update(status=quote + extra)
339 else:
340 printNicely(light_magenta('No text added.'))
341
342
343def allretweet():
344 """
345 List all retweet
346 """
347 t = Twitter(auth=authen())
348 # Get rainbow id
349 try:
350 id = int(g['stuff'].split()[0])
351 except:
352 printNicely(red('Sorry I can\'t understand.'))
353 return
354 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
355 # Get display num if exist
356 try:
357 num = int(g['stuff'].split()[1])
358 except:
359 num = c['RETWEETS_SHOW_NUM']
360 # Get result and display
361 rt_ary = t.statuses.retweets(id=tid, count=num)
362 if not rt_ary:
363 printNicely(magenta('This tweet has no retweet.'))
364 return
365 for tweet in reversed(rt_ary):
366 draw(t=tweet, iot=g['iot'])
367 printNicely('')
368
369
370def favorite():
371 """
372 Favorite
373 """
374 t = Twitter(auth=authen())
375 try:
376 id = int(g['stuff'].split()[0])
377 except:
378 printNicely(red('Sorry I can\'t understand.'))
379 return
380 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
381 t.favorites.create(_id=tid, include_entities=False)
382 printNicely(green('Favorited.'))
383 draw(t.statuses.show(id=tid), iot=g['iot'])
384 printNicely('')
385
386
387def reply():
388 """
389 Reply
390 """
391 t = Twitter(auth=authen())
392 try:
393 id = int(g['stuff'].split()[0])
394 except:
395 printNicely(red('Sorry I can\'t understand.'))
396 return
397 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
398 user = t.statuses.show(id=tid)['user']['screen_name']
399 status = ' '.join(g['stuff'].split()[1:])
400 status = '@' + user + ' ' + status.decode('utf-8')
401 t.statuses.update(status=status, in_reply_to_status_id=tid)
402
403
404def delete():
405 """
406 Delete
407 """
408 t = Twitter(auth=authen())
409 try:
410 rid = int(g['stuff'].split()[0])
411 except:
412 printNicely(red('Sorry I can\'t understand.'))
413 return
414 tid = db.rainbow_to_tweet_query(rid)[0].tweet_id
415 t.statuses.destroy(id=tid)
416 printNicely(green('Okay it\'s gone.'))
417
418
419def unfavorite():
420 """
421 Unfavorite
422 """
423 t = Twitter(auth=authen())
424 try:
425 id = int(g['stuff'].split()[0])
426 except:
427 printNicely(red('Sorry I can\'t understand.'))
428 return
429 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
430 t.favorites.destroy(_id=tid)
431 printNicely(green('Okay it\'s unfavorited.'))
432 draw(t.statuses.show(id=tid), iot=g['iot'])
433 printNicely('')
434
435
436def search():
437 """
438 Search
439 """
440 t = Twitter(auth=authen())
441 if g['stuff'].startswith('#'):
442 rel = t.search.tweets(q=g['stuff'])['statuses']
443 if rel:
444 printNicely('Newest tweets:')
445 for i in reversed(xrange(c['SEARCH_MAX_RECORD'])):
446 draw(t=rel[i],
447 iot=g['iot'],
448 keyword=g['stuff'].strip()[1:])
449 printNicely('')
450 else:
451 printNicely(magenta('I\'m afraid there is no result'))
452 else:
453 printNicely(red('A keyword should be a hashtag (like \'#AKB48\')'))
454
455
456def message():
457 """
458 Send a direct message
459 """
460 t = Twitter(auth=authen())
461 user = g['stuff'].split()[0]
462 if user[0].startswith('@'):
463 try:
464 content = g['stuff'].split()[1]
465 except:
466 printNicely(red('Sorry I can\'t understand.'))
467 t.direct_messages.new(
468 screen_name=user[1:],
469 text=content
470 )
471 printNicely(green('Message sent.'))
472 else:
473 printNicely(red('A name should begin with a \'@\''))
474
475
476def show():
477 """
478 Show image
479 """
480 t = Twitter(auth=authen())
481 try:
482 target = g['stuff'].split()[0]
483 if target != 'image':
484 return
485 id = int(g['stuff'].split()[1])
486 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
487 tweet = t.statuses.show(id=tid)
488 media = tweet['entities']['media']
489 for m in media:
490 res = requests.get(m['media_url'])
491 img = Image.open(BytesIO(res.content))
492 img.show()
493 except:
494 printNicely(red('Sorry I can\'t show this image.'))
495
496
497def urlopen():
498 """
499 Open url
500 """
501 t = Twitter(auth=authen())
502 try:
503 if not g['stuff'].isdigit():
504 return
505 tid = db.rainbow_to_tweet_query(g['stuff'])[0].tweet_id
506 tweet = t.statuses.show(id=tid)
507 link_ary = [
508 u for u in tweet['text'].split() if u.startswith('http://')]
509 if not link_ary:
510 printNicely(light_magenta('No url here @.@!'))
511 return
512 for link in link_ary:
513 webbrowser.open(link)
514 except:
515 printNicely(red('Sorry I can\'t open url in this tweet.'))
516
517
518def ls():
519 """
520 List friends for followers
521 """
522 t = Twitter(auth=authen())
523 # Get name
524 try:
525 name = g['stuff'].split()[1]
526 if name.startswith('@'):
527 name = name[1:]
528 else:
529 printNicely(red('A name should begin with a \'@\''))
530 raise Exception('Invalid name')
531 except:
532 name = g['original_name']
533 # Get list followers or friends
534 try:
535 target = g['stuff'].split()[0]
536 except:
537 printNicely(red('Omg some syntax is wrong.'))
538 # Init cursor
539 d = {'fl': 'followers', 'fr': 'friends'}
540 next_cursor = -1
541 rel = {}
542 # Cursor loop
543 while next_cursor != 0:
544 list = getattr(t, d[target]).list(
545 screen_name=name,
546 cursor=next_cursor,
547 skip_status=True,
548 include_entities=False,
549 )
550 for u in list['users']:
551 rel[u['name']] = '@' + u['screen_name']
552 next_cursor = list['next_cursor']
553 # Print out result
554 printNicely('All: ' + str(len(rel)) + ' ' + d[target] + '.')
555 for name in rel:
556 user = ' ' + cycle_color(name)
557 user += color_func(c['TWEET']['nick'])(' ' + rel[name] + ' ')
558 printNicely(user)
559
560
561def inbox():
562 """
563 Inbox direct messages
564 """
565 t = Twitter(auth=authen())
566 num = c['MESSAGES_DISPLAY']
567 rel = []
568 if g['stuff'].isdigit():
569 num = g['stuff']
570 cur_page = 1
571 # Max message per page is 20 so we have to loop
572 while num > 20:
573 rel = rel + t.direct_messages(
574 count=20,
575 page=cur_page,
576 include_entities=False,
577 skip_status=False
578 )
579 num -= 20
580 cur_page += 1
581 rel = rel + t.direct_messages(
582 count=num,
583 page=cur_page,
584 include_entities=False,
585 skip_status=False
586 )
587 # Display
588 printNicely('Inbox: newest ' + str(len(rel)) + ' messages.')
589 for m in reversed(rel):
590 print_message(m)
591 printNicely('')
592
593
594def sent():
595 """
596 Sent direct messages
597 """
598 t = Twitter(auth=authen())
599 num = c['MESSAGES_DISPLAY']
600 rel = []
601 if g['stuff'].isdigit():
602 num = int(g['stuff'])
603 cur_page = 1
604 # Max message per page is 20 so we have to loop
605 while num > 20:
606 rel = rel + t.direct_messages.sent(
607 count=20,
608 page=cur_page,
609 include_entities=False,
610 skip_status=False
611 )
612 num -= 20
613 cur_page += 1
614 rel = rel + t.direct_messages.sent(
615 count=num,
616 page=cur_page,
617 include_entities=False,
618 skip_status=False
619 )
620 # Display
621 printNicely('Sent: newest ' + str(len(rel)) + ' messages.')
622 for m in reversed(rel):
623 print_message(m)
624 printNicely('')
625
626
627def trash():
628 """
629 Remove message
630 """
631 t = Twitter(auth=authen())
632 try:
633 rid = int(g['stuff'].split()[0])
634 except:
635 printNicely(red('Sorry I can\'t understand.'))
636 mid = db.rainbow_to_message_query(rid)[0].message_id
637 t.direct_messages.destroy(id=mid)
638 printNicely(green('Message deleted.'))
639
640
641def whois():
642 """
643 Show profile of a specific user
644 """
645 t = Twitter(auth=authen())
646 screen_name = g['stuff'].split()[0]
647 if screen_name.startswith('@'):
648 try:
649 user = t.users.show(
650 screen_name=screen_name[1:],
651 include_entities=False)
652 show_profile(user, g['iot'])
653 except:
654 printNicely(red('Omg no user.'))
655 else:
656 printNicely(red('A name should begin with a \'@\''))
657
658
659def follow():
660 """
661 Follow a user
662 """
663 t = Twitter(auth=authen())
664 screen_name = g['stuff'].split()[0]
665 if screen_name.startswith('@'):
666 t.friendships.create(screen_name=screen_name[1:], follow=True)
667 printNicely(green('You are following ' + screen_name + ' now!'))
668 else:
669 printNicely(red('A name should begin with a \'@\''))
670
671
672def unfollow():
673 """
674 Unfollow a user
675 """
676 t = Twitter(auth=authen())
677 screen_name = g['stuff'].split()[0]
678 if screen_name.startswith('@'):
679 t.friendships.destroy(
680 screen_name=screen_name[1:],
681 include_entities=False)
682 printNicely(green('Unfollow ' + screen_name + ' success!'))
683 else:
684 printNicely(red('A name should begin with a \'@\''))
685
686
687def mute():
688 """
689 Mute a user
690 """
691 t = Twitter(auth=authen())
692 try:
693 screen_name = g['stuff'].split()[0]
694 except:
695 printNicely(red('A name should be specified. '))
696 return
697 if screen_name.startswith('@'):
698 rel = t.mutes.users.create(screen_name=screen_name[1:])
699 if isinstance(rel, dict):
700 printNicely(green(screen_name + ' is muted.'))
701 else:
702 printNicely(red(rel))
703 else:
704 printNicely(red('A name should begin with a \'@\''))
705
706
707def unmute():
708 """
709 Unmute a user
710 """
711 t = Twitter(auth=authen())
712 try:
713 screen_name = g['stuff'].split()[0]
714 except:
715 printNicely(red('A name should be specified. '))
716 return
717 if screen_name.startswith('@'):
718 rel = t.mutes.users.destroy(screen_name=screen_name[1:])
719 if isinstance(rel, dict):
720 printNicely(green(screen_name + ' is unmuted.'))
721 else:
722 printNicely(red(rel))
723 else:
724 printNicely(red('A name should begin with a \'@\''))
725
726
727def muting():
728 """
729 List muting user
730 """
731 t = Twitter(auth=authen())
732 # Init cursor
733 next_cursor = -1
734 rel = {}
735 # Cursor loop
736 while next_cursor != 0:
737 list = t.mutes.users.list(
738 screen_name=g['original_name'],
739 cursor=next_cursor,
740 skip_status=True,
741 include_entities=False,
742 )
743 for u in list['users']:
744 rel[u['name']] = '@' + u['screen_name']
745 next_cursor = list['next_cursor']
746 # Print out result
747 printNicely('All: ' + str(len(rel)) + ' people.')
748 for name in rel:
749 user = ' ' + cycle_color(name)
750 user += color_func(c['TWEET']['nick'])(' ' + rel[name] + ' ')
751 printNicely(user)
752
753
754def block():
755 """
756 Block a user
757 """
758 t = Twitter(auth=authen())
759 screen_name = g['stuff'].split()[0]
760 if screen_name.startswith('@'):
761 t.blocks.create(
762 screen_name=screen_name[1:],
763 include_entities=False,
764 skip_status=True)
765 printNicely(green('You blocked ' + screen_name + '.'))
766 else:
767 printNicely(red('A name should begin with a \'@\''))
768
769
770def unblock():
771 """
772 Unblock a user
773 """
774 t = Twitter(auth=authen())
775 screen_name = g['stuff'].split()[0]
776 if screen_name.startswith('@'):
777 t.blocks.destroy(
778 screen_name=screen_name[1:],
779 include_entities=False,
780 skip_status=True)
781 printNicely(green('Unblock ' + screen_name + ' success!'))
782 else:
783 printNicely(red('A name should begin with a \'@\''))
784
785
786def report():
787 """
788 Report a user as a spam account
789 """
790 t = Twitter(auth=authen())
791 screen_name = g['stuff'].split()[0]
792 if screen_name.startswith('@'):
793 t.users.report_spam(
794 screen_name=screen_name[1:])
795 printNicely(green('You reported ' + screen_name + '.'))
796 else:
797 printNicely(red('Sorry I can\'t understand.'))
798
799
800def get_slug():
801 """
802 Get Slug Decorator
803 """
804 # Get list name
805 list_name = raw_input(light_magenta('Give me the list\'s name: '))
806 # Get list name and owner
807 try:
808 owner, slug = list_name.split('/')
809 if slug.startswith('@'):
810 slug = slug[1:]
811 return owner, slug
812 except:
813 printNicely(light_magenta('List name should follow "@owner/list_name" format.'))
814 raise Exception('Wrong list name')
815
816
817def show_lists(t):
818 """
819 List list
820 """
821 rel = t.lists.list(screen_name=g['original_name'])
822 if rel:
823 print_list(rel)
824 else:
825 printNicely(light_magenta('You belong to no lists :)'))
826
827
828def list_home(t):
829 """
830 List home
831 """
832 owner, slug = get_slug()
833 res = t.lists.statuses(
834 slug=slug,
835 owner_screen_name=owner,
836 count=c['LIST_MAX'],
837 include_entities=False)
838 for tweet in res:
839 draw(t=tweet)
840 printNicely('')
841
842
843def list_members(t):
844 """
845 List members
846 """
847 owner, slug = get_slug()
848 # Get members
849 rel = {}
850 next_cursor = -1
851 while next_cursor != 0:
852 m = t.lists.members(
853 slug=slug,
854 owner_screen_name=owner,
855 cursor=next_cursor,
856 include_entities=False)
857 for u in m['users']:
858 rel[u['name']] = '@' + u['screen_name']
859 next_cursor = m['next_cursor']
860 printNicely('All: ' + str(len(rel)) + ' members.')
861 for name in rel:
862 user = ' ' + cycle_color(name)
863 user += color_func(c['TWEET']['nick'])(' ' + rel[name] + ' ')
864 printNicely(user)
865
866
867def list_subscribers(t):
868 """
869 List subscribers
870 """
871 owner, slug = get_slug()
872 # Get subscribers
873 rel = {}
874 next_cursor = -1
875 while next_cursor != 0:
876 m = t.lists.subscribers(
877 slug=slug,
878 owner_screen_name=owner,
879 cursor=next_cursor,
880 include_entities=False)
881 for u in m['users']:
882 rel[u['name']] = '@' + u['screen_name']
883 next_cursor = m['next_cursor']
884 printNicely('All: ' + str(len(rel)) + ' subscribers.')
885 for name in rel:
886 user = ' ' + cycle_color(name)
887 user += color_func(c['TWEET']['nick'])(' ' + rel[name] + ' ')
888 printNicely(user)
889
890
891def list_add(t):
892 """
893 Add specific user to a list
894 """
895 owner, slug = get_slug()
896 # Add
897 user_name = raw_input(light_magenta('Give me name of the newbie: '))
898 if user_name.startswith('@'):
899 user_name = user_name[1:]
900 try:
901 t.lists.members.create(
902 slug=slug,
903 owner_screen_name=owner,
904 screen_name=user_name)
905 printNicely(light_green('Added.'))
906 except:
907 printNicely(light_magenta('I\'m sorry we can not add him/her.'))
908
909
910def list_remove(t):
911 """
912 Remove specific user from a list
913 """
914 owner, slug = get_slug()
915 # Remove
916 user_name = raw_input(light_magenta('Give me name of the unlucky one: '))
917 if user_name.startswith('@'):
918 user_name = user_name[1:]
919 try:
920 t.lists.members.destroy(
921 slug=slug,
922 owner_screen_name=owner,
923 screen_name=user_name)
924 printNicely(light_green('Gone.'))
925 except:
926 printNicely(light_magenta('I\'m sorry we can not remove him/her.'))
927
928
929def list_subscribe(t):
930 """
931 Subscribe to a list
932 """
933 owner, slug = get_slug()
934 # Subscribe
935 try:
936 t.lists.subscribers.create(
937 slug=slug,
938 owner_screen_name=owner)
939 printNicely(light_green('Done.'))
940 except:
941 printNicely(
942 light_magenta('I\'m sorry you can not subscribe to this list.'))
943
944
945def list_unsubscribe(t):
946 """
947 Unsubscribe a list
948 """
949 owner, slug = get_slug()
950 # Subscribe
951 try:
952 t.lists.subscribers.destroy(
953 slug=slug,
954 owner_screen_name=owner)
955 printNicely(light_green('Done.'))
956 except:
957 printNicely(
958 light_magenta('I\'m sorry you can not unsubscribe to this list.'))
959
960
961def list_own(t):
962 """
963 List own
964 """
965 rel = []
966 next_cursor = -1
967 while next_cursor != 0:
968 res = t.lists.ownerships(
969 screen_name=g['original_name'],
970 cursor=next_cursor)
971 rel += res['lists']
972 next_cursor = res['next_cursor']
973 if rel:
974 print_list(rel)
975 else:
976 printNicely(light_magenta('You own no lists :)'))
977
978
979def list_new(t):
980 """
981 Create a new list
982 """
983 name = raw_input(light_magenta('New list\'s name: '))
984 mode = raw_input(light_magenta('New list\'s mode (public/private): '))
985 description = raw_input(light_magenta('New list\'s description: '))
986 try:
987 t.lists.create(
988 name=name,
989 mode=mode,
990 description=description)
991 printNicely(light_green(name + ' list is created.'))
992 except:
993 printNicely(red('Oops something is wrong with Twitter :('))
994
995
996def list_update(t):
997 """
998 Update a list
999 """
1000 slug = raw_input(light_magenta('Your list that you want to update: '))
1001 name = raw_input(light_magenta('Update name (leave blank to unchange): '))
1002 mode = raw_input(light_magenta('Update mode (public/private): '))
1003 description = raw_input(light_magenta('Update description: '))
1004 try:
1005 if name:
1006 t.lists.update(
1007 slug='-'.join(slug.split()),
1008 owner_screen_name=g['original_name'],
1009 name=name,
1010 mode=mode,
1011 description=description)
1012 else:
1013 t.lists.update(
1014 slug=slug,
1015 owner_screen_name=g['original_name'],
1016 mode=mode,
1017 description=description)
1018 printNicely(light_green(slug + ' list is updated.'))
1019 except:
1020 printNicely(red('Oops something is wrong with Twitter :('))
1021
1022
1023def list_delete(t):
1024 """
1025 Delete a list
1026 """
1027 slug = raw_input(light_magenta('Your list that you want to update: '))
1028 try:
1029 t.lists.destroy(
1030 slug='-'.join(slug.split()),
1031 owner_screen_name=g['original_name'])
1032 printNicely(light_green(slug + ' list is deleted.'))
1033 except:
1034 printNicely(red('Oops something is wrong with Twitter :('))
1035
1036
1037def list():
1038 """
1039 Twitter's list
1040 """
1041 t = Twitter(auth=authen())
1042 # List all lists or base on action
1043 try:
1044 g['list_action'] = g['stuff'].split()[0]
1045 except:
1046 show_lists(t)
1047 return
1048 # Sub-function
1049 action_ary = {
1050 'home': list_home,
1051 'all_mem': list_members,
1052 'all_sub': list_subscribers,
1053 'add': list_add,
1054 'rm': list_remove,
1055 'sub': list_subscribe,
1056 'unsub': list_unsubscribe,
1057 'own': list_own,
1058 'new': list_new,
1059 'update': list_update,
1060 'del': list_delete,
1061 }
1062 try:
1063 return action_ary[g['list_action']](t)
1064 except:
1065 printNicely(red('Please try again.'))
1066
1067
1068def cal():
1069 """
1070 Unix's command `cal`
1071 """
1072 # Format
1073 rel = os.popen('cal').read().split('\n')
1074 month = rel.pop(0)
1075 date = rel.pop(0)
1076 show_calendar(month, date, rel)
1077
1078
1079def theme():
1080 """
1081 List and change theme
1082 """
1083 if not g['stuff']:
1084 # List themes
1085 for theme in g['themes']:
1086 line = light_magenta(theme)
1087 if c['THEME'] == theme:
1088 line = ' ' * 2 + light_yellow('* ') + line
1089 else:
1090 line = ' ' * 4 + line
1091 printNicely(line)
1092 elif g['stuff'] == 'current_as_default':
1093 # Set as default
1094 def fixup(adict, k, v):
1095 for key in adict.keys():
1096 if key == k:
1097 adict[key] = v
1098 elif type(adict[key]) is dict:
1099 fixup(adict[key], k, v)
1100 # Modify
1101 path = os.environ.get(
1102 'HOME',
1103 os.environ.get(
1104 'USERPROFILE',
1105 '')) + os.sep + '.rainbow_config.json'
1106 data = load_config(rainbow_config)
1107 fixup(data, 'THEME', c['THEME'])
1108 # Save
1109 with open(path, 'w') as out:
1110 json.dump(data, out, indent = 4)
1111 os.system('chmod 777 ' + path)
1112 printNicely(light_green('Okay it will be applied from next time :)'))
1113 else:
1114 # Change theme
1115 try:
1116 # Load new config
1117 if g['stuff'] != 'custom':
1118 new_config = os.path.dirname(
1119 __file__) + '/colorset/' + g['stuff'] + '.json'
1120 else:
1121 new_config = os.environ.get(
1122 'HOME', os.environ.get(
1123 'USERPROFILE',
1124 '')) + os.sep + '.rainbow_config.json'
1125 new_config = load_config(new_config)
1126 if new_config:
1127 for nc in new_config:
1128 c[nc] = new_config[nc]
1129 # Update db and reset colors
1130 db.theme_update(g['stuff'])
1131 c['THEME'] = g['stuff']
1132 reset_cycle()
1133 g['decorated_name'] = color_func(
1134 c['DECORATED_NAME'])(
1135 '[@' + g['original_name'] + ']: ')
1136 printNicely(green('Theme changed.'))
1137 except:
1138 printNicely(red('No such theme exists.'))
1139
1140
1141def help_discover():
1142 """
1143 Discover the world
1144 """
1145 s = ' ' * 2
1146 # Discover the world
1147 usage = '\n'
1148 usage += s + grey(u'\u266A' + ' Discover the world \n')
1149 usage += s * 2 + light_green('trend') + ' will show global trending topics. ' + \
1150 'You can try ' + light_green('trend US') + ' or ' + \
1151 light_green('trend JP Tokyo') + '.\n'
1152 usage += s * 2 + light_green('home') + ' will show your timeline. ' + \
1153 light_green('home 7') + ' will show 7 tweets.\n'
1154 usage += s * 2 + light_green('mentions') + ' will show mentions timeline. ' + \
1155 light_green('mentions 7') + ' will show 7 mention tweets.\n'
1156 usage += s * 2 + light_green('whois @mdo') + ' will show profile of ' + \
1157 magenta('@mdo') + '.\n'
1158 usage += s * 2 + light_green('view @mdo') + \
1159 ' will show ' + magenta('@mdo') + '\'s home.\n'
1160 usage += s * 2 + light_green('s #AKB48') + ' will search for "' + \
1161 light_yellow('AKB48') + '" and return 5 newest tweet.\n'
1162 printNicely(usage)
1163
1164
1165def help_tweets():
1166 """
1167 Tweets
1168 """
1169 s = ' ' * 2
1170 # Tweet
1171 usage = '\n'
1172 usage += s + grey(u'\u266A' + ' Tweets \n')
1173 usage += s * 2 + light_green('t oops ') + \
1174 'will tweet "' + light_yellow('oops') + '" immediately.\n'
1175 usage += s * 2 + \
1176 light_green('rt 12 ') + ' will retweet to tweet with ' + \
1177 light_yellow('[id=12]') + '.\n'
1178 usage += s * 2 + \
1179 light_green('quote 12 ') + ' will quote the tweet with ' + \
1180 light_yellow('[id=12]') + '. If no extra text is added, ' + \
1181 'the quote will be canceled.\n'
1182 usage += s * 2 + \
1183 light_green('allrt 12 20 ') + ' will list 20 newest retweet of the tweet with ' + \
1184 light_yellow('[id=12]') + '.\n'
1185 usage += s * 2 + light_green('rep 12 oops') + ' will reply "' + \
1186 light_yellow('oops') + '" to tweet with ' + \
1187 light_yellow('[id=12]') + '.\n'
1188 usage += s * 2 + \
1189 light_green('fav 12 ') + ' will favorite the tweet with ' + \
1190 light_yellow('[id=12]') + '.\n'
1191 usage += s * 2 + \
1192 light_green('ufav 12 ') + ' will unfavorite tweet with ' + \
1193 light_yellow('[id=12]') + '.\n'
1194 usage += s * 2 + \
1195 light_green('del 12 ') + ' will delete tweet with ' + \
1196 light_yellow('[id=12]') + '.\n'
1197 usage += s * 2 + light_green('show image 12') + ' will show image in tweet with ' + \
1198 light_yellow('[id=12]') + ' in your OS\'s image viewer.\n'
1199 usage += s * 2 + light_green('open 12') + ' will open url in tweet with ' + \
1200 light_yellow('[id=12]') + ' in your OS\'s default browser.\n'
1201 printNicely(usage)
1202
1203
1204def help_messages():
1205 """
1206 Messages
1207 """
1208 s = ' ' * 2
1209 # Direct message
1210 usage = '\n'
1211 usage += s + grey(u'\u266A' + ' Direct messages \n')
1212 usage += s * 2 + light_green('inbox') + ' will show inbox messages. ' + \
1213 light_green('inbox 7') + ' will show newest 7 messages.\n'
1214 usage += s * 2 + light_green('sent') + ' will show sent messages. ' + \
1215 light_green('sent 7') + ' will show newest 7 messages.\n'
1216 usage += s * 2 + light_green('mes @dtvd88 hi') + ' will send a "hi" messege to ' + \
1217 magenta('@dtvd88') + '.\n'
1218 usage += s * 2 + light_green('trash 5') + ' will remove message with ' + \
1219 light_yellow('[message_id=5]') + '.\n'
1220 printNicely(usage)
1221
1222
1223def help_friends_and_followers():
1224 """
1225 Friends and Followers
1226 """
1227 s = ' ' * 2
1228 # Follower and following
1229 usage = '\n'
1230 usage += s + grey(u'\u266A' + ' Friends and followers \n')
1231 usage += s * 2 + \
1232 light_green('ls fl') + \
1233 ' will list all followers (people who are following you).\n'
1234 usage += s * 2 + \
1235 light_green('ls fr') + \
1236 ' will list all friends (people who you are following).\n'
1237 usage += s * 2 + light_green('fl @dtvd88') + ' will follow ' + \
1238 magenta('@dtvd88') + '.\n'
1239 usage += s * 2 + light_green('ufl @dtvd88') + ' will unfollow ' + \
1240 magenta('@dtvd88') + '.\n'
1241 usage += s * 2 + light_green('mute @dtvd88') + ' will mute ' + \
1242 magenta('@dtvd88') + '.\n'
1243 usage += s * 2 + light_green('unmute @dtvd88') + ' will unmute ' + \
1244 magenta('@dtvd88') + '.\n'
1245 usage += s * 2 + light_green('muting') + ' will list muting users.\n'
1246 usage += s * 2 + light_green('block @dtvd88') + ' will block ' + \
1247 magenta('@dtvd88') + '.\n'
1248 usage += s * 2 + light_green('unblock @dtvd88') + ' will unblock ' + \
1249 magenta('@dtvd88') + '.\n'
1250 usage += s * 2 + light_green('report @dtvd88') + ' will report ' + \
1251 magenta('@dtvd88') + ' as a spam account.\n'
1252 printNicely(usage)
1253
1254
1255def help_list():
1256 """
1257 Lists
1258 """
1259 s = ' ' * 2
1260 # Twitter list
1261 usage = '\n'
1262 usage += s + grey(u'\u266A' + ' Twitter list\n')
1263 usage += s * 2 + light_green('list') + \
1264 ' will show all lists you are belong to.\n'
1265 usage += s * 2 + light_green('list home') + \
1266 ' will show timeline of list. You will be asked for list\'s name.\n'
1267 usage += s * 2 + light_green('list all_mem') + \
1268 ' will show list\'s all members.\n'
1269 usage += s * 2 + light_green('list all_sub') + \
1270 ' will show list\'s all subscribers.\n'
1271 usage += s * 2 + light_green('list add') + \
1272 ' will add specific person to a list owned by you.' + \
1273 ' You will be asked for list\'s name and person\'s name.\n'
1274 usage += s * 2 + light_green('list rm') + \
1275 ' will remove specific person from a list owned by you.' + \
1276 ' You will be asked for list\'s name and person\'s name.\n'
1277 usage += s * 2 + light_green('list sub') + \
1278 ' will subscribe you to a specific list.\n'
1279 usage += s * 2 + light_green('list unsub') + \
1280 ' will unsubscribe you from a specific list.\n'
1281 usage += s * 2 + light_green('list own') + \
1282 ' will show all list owned by you.\n'
1283 usage += s * 2 + light_green('list new') + \
1284 ' will create a new list.\n'
1285 usage += s * 2 + light_green('list update') + \
1286 ' will update a list owned by you.\n'
1287 usage += s * 2 + light_green('list del') + \
1288 ' will delete a list owned by you.\n'
1289 printNicely(usage)
1290
1291
1292def help_stream():
1293 """
1294 Stream switch
1295 """
1296 s = ' ' * 2
1297 # Switch
1298 usage = '\n'
1299 usage += s + grey(u'\u266A' + ' Switching streams \n')
1300 usage += s * 2 + light_green('switch public #AKB') + \
1301 ' will switch to public stream and follow "' + \
1302 light_yellow('AKB') + '" keyword.\n'
1303 usage += s * 2 + light_green('switch mine') + \
1304 ' will switch to your personal stream.\n'
1305 usage += s * 2 + light_green('switch mine -f ') + \
1306 ' will prompt to enter the filter.\n'
1307 usage += s * 3 + light_yellow('Only nicks') + \
1308 ' filter will decide nicks will be INCLUDE ONLY.\n'
1309 usage += s * 3 + light_yellow('Ignore nicks') + \
1310 ' filter will decide nicks will be EXCLUDE.\n'
1311 usage += s * 2 + light_green('switch mine -d') + \
1312 ' will use the config\'s ONLY_LIST and IGNORE_LIST.\n'
1313 printNicely(usage)
1314
1315
1316def help():
1317 """
1318 Help
1319 """
1320 s = ' ' * 2
1321 h, w = os.popen('stty size', 'r').read().split()
1322
1323 # Start
1324 usage = '\n'
1325 usage += s + 'Hi boss! I\'m ready to serve you right now!\n'
1326 usage += s + '-' * (int(w) - 4) + '\n'
1327 usage += s + 'You are ' + \
1328 light_yellow('already') + ' on your personal stream.\n'
1329 usage += s + 'Any update from Twitter will show up ' + \
1330 light_yellow('immediately') + '.\n'
1331 usage += s + 'In addtion, following commands are available right now:\n'
1332
1333 # Twitter help section
1334 usage += '\n'
1335 usage += s + grey(u'\u266A' + ' Twitter help\n')
1336 usage += s * 2 + light_green('h discover') + \
1337 ' will show help for discover commands.\n'
1338 usage += s * 2 + light_green('h tweets') + \
1339 ' will show help for tweets commands.\n'
1340 usage += s * 2 + light_green('h messages') + \
1341 ' will show help for messages commands.\n'
1342 usage += s * 2 + light_green('h friends_and_followers') + \
1343 ' will show help for friends and followers commands.\n'
1344 usage += s * 2 + light_green('h list') + \
1345 ' will show help for list commands.\n'
1346 usage += s * 2 + light_green('h stream') + \
1347 ' will show help for stream commands.\n'
1348
1349 # Smart shell
1350 usage += '\n'
1351 usage += s + grey(u'\u266A' + ' Smart shell\n')
1352 usage += s * 2 + light_green('111111 * 9 / 7') + ' or any math expression ' + \
1353 'will be evaluate by Python interpreter.\n'
1354 usage += s * 2 + 'Even ' + light_green('cal') + ' will show the calendar' + \
1355 ' for current month.\n'
1356
1357 # Screening
1358 usage += '\n'
1359 usage += s + grey(u'\u266A' + ' Screening \n')
1360 usage += s * 2 + light_green('theme') + ' will list available theme.' + \
1361 light_green('theme monokai') + ' will apply ' + light_yellow('monokai') + \
1362 ' theme immediately.\n'
1363 usage += s * 2 + light_green('h') + ' will show this help again.\n'
1364 usage += s * 2 + light_green('c') + ' will clear the screen.\n'
1365 usage += s * 2 + light_green('q') + ' will quit.\n'
1366
1367 # End
1368 usage += '\n'
1369 usage += s + '-' * (int(w) - 4) + '\n'
1370 usage += s + 'Have fun and hang tight! \n'
1371
1372 # Show help
1373 d = {
1374 'discover': help_discover,
1375 'tweets': help_tweets,
1376 'messages': help_messages,
1377 'friends_and_followers': help_friends_and_followers,
1378 'list': help_list,
1379 'stream': help_stream,
1380 }
1381 if g['stuff']:
1382 d[g['stuff'].strip()]()
1383 else:
1384 printNicely(usage)
1385
1386
1387def clear():
1388 """
1389 Clear screen
1390 """
1391 os.system('clear')
1392
1393
1394def quit():
1395 """
1396 Exit all
1397 """
1398 save_history()
1399 os.system('rm -rf rainbow.db')
1400 os.kill(g['stream_pid'], signal.SIGKILL)
1401 sys.exit()
1402
1403
1404def reset():
1405 """
1406 Reset prefix of line
1407 """
1408 if g['reset']:
1409 printNicely(magenta('Need tips ? Type "h" and hit Enter key!'))
1410 g['reset'] = False
1411 try:
1412 printNicely(str(eval(g['cmd'])))
1413 except Exception:
1414 pass
1415
1416
1417def process(cmd):
1418 """
1419 Process switch
1420 """
1421 return dict(zip(
1422 cmdset,
1423 [
1424 switch,
1425 trend,
1426 home,
1427 view,
1428 mentions,
1429 tweet,
1430 retweet,
1431 quote,
1432 allretweet,
1433 favorite,
1434 reply,
1435 delete,
1436 unfavorite,
1437 search,
1438 message,
1439 show,
1440 urlopen,
1441 ls,
1442 inbox,
1443 sent,
1444 trash,
1445 whois,
1446 follow,
1447 unfollow,
1448 mute,
1449 unmute,
1450 muting,
1451 block,
1452 unblock,
1453 report,
1454 list,
1455 cal,
1456 theme,
1457 help,
1458 clear,
1459 quit
1460 ]
1461 )).get(cmd, reset)
1462
1463
1464def listen():
1465 """
1466 Listen to user's input
1467 """
1468 d = dict(zip(
1469 cmdset,
1470 [
1471 ['public', 'mine'], # switch
1472 [], # trend
1473 [], # home
1474 ['@'], # view
1475 [], # mentions
1476 [], # tweet
1477 [], # retweet
1478 [], # quote
1479 [], # allretweet
1480 [], # favorite
1481 [], # reply
1482 [], # delete
1483 [], # unfavorite
1484 ['#'], # search
1485 ['@'], # message
1486 ['image'], # show image
1487 [''], # open url
1488 ['fl', 'fr'], # list
1489 [], # inbox
1490 [], # sent
1491 [], # trash
1492 ['@'], # whois
1493 ['@'], # follow
1494 ['@'], # unfollow
1495 ['@'], # mute
1496 ['@'], # unmute
1497 ['@'], # muting
1498 ['@'], # block
1499 ['@'], # unblock
1500 ['@'], # report
1501 [
1502 'home',
1503 'all_mem',
1504 'all_sub',
1505 'add',
1506 'rm',
1507 'sub',
1508 'unsub',
1509 'own',
1510 'new',
1511 'update',
1512 'del'
1513 ], # list
1514 [], # cal
1515 g['themes'] + ['current_as_default'], # theme
1516 [
1517 'discover',
1518 'tweets',
1519 'messages',
1520 'friends_and_followers',
1521 'list',
1522 'stream'
1523 ], # help
1524 [], # clear
1525 [], # quit
1526 ]
1527 ))
1528 init_interactive_shell(d)
1529 read_history()
1530 reset()
1531 while True:
1532 if g['prefix']:
1533 line = raw_input(g['decorated_name'])
1534 else:
1535 line = raw_input()
1536 try:
1537 cmd = line.split()[0]
1538 except:
1539 cmd = ''
1540 g['cmd'] = cmd
1541 # Save cmd to global variable and call process
1542 try:
1543 g['stuff'] = ' '.join(line.split()[1:])
1544 process(cmd)()
1545 except Exception:
1546 printNicely(red('OMG something is wrong with Twitter right now.'))
1547 # Not redisplay prefix
1548 if cmd in ['switch', 't', 'rt', 'rep']:
1549 g['prefix'] = False
1550 else:
1551 g['prefix'] = True
1552
1553
1554def stream(domain, args, name='Rainbow Stream'):
1555 """
1556 Track the stream
1557 """
1558
1559 # The Logo
1560 art_dict = {
1561 c['USER_DOMAIN']: name,
1562 c['PUBLIC_DOMAIN']: args.track_keywords,
1563 c['SITE_DOMAIN']: name,
1564 }
1565 if c['ASCII_ART']:
1566 ascii_art(art_dict[domain])
1567
1568 # These arguments are optional:
1569 stream_args = dict(
1570 timeout=args.timeout,
1571 block=not args.no_block,
1572 heartbeat_timeout=args.heartbeat_timeout)
1573
1574 # Track keyword
1575 query_args = dict()
1576 if args.track_keywords:
1577 query_args['track'] = args.track_keywords
1578
1579 # Get stream
1580 stream = TwitterStream(
1581 auth=authen(),
1582 domain=domain,
1583 **stream_args)
1584
1585 try:
1586 if domain == c['USER_DOMAIN']:
1587 tweet_iter = stream.user(**query_args)
1588 elif domain == c['SITE_DOMAIN']:
1589 tweet_iter = stream.site(**query_args)
1590 else:
1591 if args.track_keywords:
1592 tweet_iter = stream.statuses.filter(**query_args)
1593 else:
1594 tweet_iter = stream.statuses.sample()
1595
1596 # Iterate over the stream.
1597 for tweet in tweet_iter:
1598 if tweet is None:
1599 printNicely("-- None --")
1600 elif tweet is Timeout:
1601 printNicely("-- Timeout --")
1602 elif tweet is HeartbeatTimeout:
1603 printNicely("-- Heartbeat Timeout --")
1604 elif tweet is Hangup:
1605 printNicely("-- Hangup --")
1606 elif tweet.get('text'):
1607 draw(
1608 t=tweet,
1609 iot=args.image_on_term,
1610 keyword=args.track_keywords,
1611 fil=args.filter,
1612 ig=args.ignore,
1613 )
1614 except TwitterHTTPError:
1615 printNicely('')
1616 printNicely(
1617 magenta("We have maximum connection problem with twitter'stream API right now :("))
1618
1619
1620def fly():
1621 """
1622 Main function
1623 """
1624 # Spawn stream process
1625 args = parse_arguments()
1626 try:
1627 get_decorated_name()
1628
1629 except TwitterHTTPError:
1630 printNicely('')
1631 printNicely(
1632 magenta("Something wrong with Twitter Oauth right now :("))
1633 printNicely(
1634 magenta("Please delete ~/.rainbow_oauth and try again."))
1635 save_history()
1636 os.system('rm -rf rainbow.db')
1637 sys.exit()
1638
1639 p = Process(
1640 target=stream,
1641 args=(
1642 c['USER_DOMAIN'],
1643 args,
1644 g['original_name']))
1645 p.start()
1646
1647 # Start listen process
1648 time.sleep(0.5)
1649 g['reset'] = True
1650 g['prefix'] = True
1651 g['stream_pid'] = p.pid
1652 g['iot'] = args.image_on_term
1653 listen()