Last active
June 11, 2025 20:10
-
-
Save tung/20de3e992ca3a6629843e8169dc0398e to your computer and use it in GitHub Desktop.
Revisions
-
tung revised this gist
Mar 10, 2023 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -79,6 +79,8 @@ def badge_icons(message): def simple_name(commenter): if commenter == None: return "(null)" name = commenter['login'] display_name = commenter['displayName'] if display_name: -
tung revised this gist
Dec 16, 2022 . 1 changed file with 41 additions and 33 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -49,8 +49,8 @@ def message_color(comment): r = 128 g = 128 b = 128 if 'userColor' in comment['message'] and comment['message']['userColor']: user_color = comment['message']['userColor'] if len(user_color) == 7 and user_color[0] == '#': r = int(user_color[1:3], 16) g = int(user_color[3:5], 16) @@ -65,22 +65,22 @@ def message_color(comment): def badge_icons(message): b = '' if 'userBadges' in message: for badge in message['userBadges']: if badge['setID'] == 'broadcaster': b += '🎥' elif badge['setID'] == 'moderator': b += '⚔' elif badge['setID'] == 'subscriber': b += '★' elif badge['setID'] == 'staff': b += '⛨' return b def simple_name(commenter): name = commenter['login'] display_name = commenter['displayName'] if display_name: c = display_name[0].lower() if (c >= 'a' and c <= 'z') or (c >= '0' and c <= '9') or c == '_': @@ -89,20 +89,20 @@ def simple_name(commenter): def format_message(comment): time = comment['contentOffsetSeconds'] badges = badge_icons(comment['message']) name = simple_name(comment['commenter']) color = lighten_color(message_color(comment)) message = "".join([f['text'] for f in comment['message']['fragments']]) #if comment['message']['is_action']: # message = '\033[38;2;' + str(color.r) + ';' + str(color.g) + ';' + str(color.b) + 'm' + message + '\033[0m' nick = '\033[38;2;{c.r};{c.g};{c.b}m<{badges}{name}>\033[0m'.format(badges=badges, name=name, c=color) if 'userBadges' in comment['message']: is_broadcaster = False for badge in comment['message']['userBadges']: if badge['setID'] == 'broadcaster': is_broadcaster = True break if is_broadcaster: @@ -112,12 +112,10 @@ def format_message(comment): def print_response_messages(data, start): for comment in data['comments']['edges']: if comment['node']['contentOffsetSeconds'] < start: continue print(format_message(comment['node'])) ################################################################################ @@ -129,28 +127,38 @@ def print_response_messages(data, start): start = int(sys.argv[2]) session = requests.Session() session.headers = { 'Client-ID': os.environ['TWITCH_CLIENT_ID'], 'content-type': 'application/json' } response = session.post( 'https://gql.twitch.tv/gql', "[{\"operationName\":\"VideoCommentsByOffsetOrCursor\"," + "\"variables\":{\"videoID\":\"" + video_id + "\",\"contentOffsetSeconds\":" + str(start) + "}," + "\"extensions\":{\"persistedQuery\":{\"version\":1,\"sha256Hash\":\"b70a3591ff0f4e0313d126c6a1502d79a1c02baebb288227c582044aa76adf6a\"}}}]", timeout=10) response.raise_for_status() data = response.json() print_response_messages(data[0]['data']['video'], start) cursor = None if data[0]['data']['video']['comments']['pageInfo']['hasNextPage']: cursor = data[0]['data']['video']['comments']['edges'][-1]['cursor'] time.sleep(0.1) while cursor: response = session.post( 'https://gql.twitch.tv/gql', "[{\"operationName\":\"VideoCommentsByOffsetOrCursor\"," + "\"variables\":{\"videoID\":\"" + video_id + "\",\"cursor\":\"" + cursor + "\"}," + "\"extensions\":{\"persistedQuery\":{\"version\":1,\"sha256Hash\":\"b70a3591ff0f4e0313d126c6a1502d79a1c02baebb288227c582044aa76adf6a\"}}}]", timeout=10) response.raise_for_status() data = response.json() print_response_messages(data[0]['data']['video'], start) if data[0]['data']['video']['comments']['pageInfo']['hasNextPage']: cursor = data[0]['data']['video']['comments']['edges'][-1]['cursor'] time.sleep(0.1) else: cursor = None -
tung revised this gist
Oct 12, 2017 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -96,7 +96,7 @@ def format_message(comment): message = comment['message']['body'] if comment['message']['is_action']: message = '\033[38;2;' + str(color.r) + ';' + str(color.g) + ';' + str(color.b) + 'm' + message + '\033[0m' nick = '\033[38;2;{c.r};{c.g};{c.b}m<{badges}{name}>\033[0m'.format(badges=badges, name=name, c=color) if 'user_badges' in comment['message']: -
tung created this gist
Oct 11, 2017 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,156 @@ #!/usr/bin/env python3 # # A script to download chat from a Twitch VOD and print it to a terminal. # Chat will be downloaded all the way until it ends. # # Usage: TWITCH_CLIENT_ID=0123456789abcdef0123456789abcde twitch-vod-chat.py [video_id] [start] # # This script could break at any time, because Twitch's chat API is # undocumented and likes to change at any time; in fact, this script was # created because Twitch got rid of rechat.twitch.tv. # import json import os import requests import sys import time import types def time_to_hhmmss(t): hours = int(t // 3600) minutes = int((t - hours * 3600) // 60) seconds = int(t - hours * 3600 - minutes * 60) milliseconds = int((t - hours * 3600 - minutes * 60 - seconds) * 1000) return "{0}:{1:02}:{2:02}.{3:<03}".format(hours, minutes, seconds, milliseconds) def lighten_color(color): r = color.r * 3 // 4 + 63 g = color.g * 3 // 4 + 63 b = color.b * 3 // 4 + 63 return types.SimpleNamespace(r=r, g=g, b=b) def message_color(comment): color_by_name = { 'white': (255, 255, 255), 'black': (0, 0, 0), 'red': (255, 0, 0), 'green': (0, 255, 0), 'blue': (0, 0, 255), 'yellow': (255, 255, 0), 'gray': (128, 128, 128), 'magenta': (255, 0, 255), 'cyan': (0, 255, 255) } r = 128 g = 128 b = 128 if 'user_color' in comment['message']: user_color = comment['message']['user_color'] if len(user_color) == 7 and user_color[0] == '#': r = int(user_color[1:3], 16) g = int(user_color[3:5], 16) b = int(user_color[5:7], 16) elif user_color in color_by_name: c = color_by_name[user_color] r = c[0] g = c[1] b = c[2] return types.SimpleNamespace(r=r, g=g, b=b) def badge_icons(message): b = '' if 'user_badges' in message: for badge in message['user_badges']: if badge['_id'] == 'broadcaster': b += '🎥' elif badge['_id'] == 'moderator': b += '⚔' elif badge['_id'] == 'subscriber': b += '★' elif badge['_id'] == 'staff': b += '⛨' return b def simple_name(commenter): name = commenter['name'] display_name = commenter['display_name'] if display_name: c = display_name[0].lower() if (c >= 'a' and c <= 'z') or (c >= '0' and c <= '9') or c == '_': name = display_name return name def format_message(comment): time = comment['content_offset_seconds'] badges = badge_icons(comment['message']) name = simple_name(comment['commenter']) color = lighten_color(message_color(comment)) message = comment['message']['body'] if comment['message']['is_action']: message = '\033[38;2;' + color.r + ';' + color.g + ';' + color.b + 'm' + message + '\033[0m' nick = '\033[38;2;{c.r};{c.g};{c.b}m<{badges}{name}>\033[0m'.format(badges=badges, name=name, c=color) if 'user_badges' in comment['message']: is_broadcaster = False for badge in comment['message']['user_badges']: if badge['_id'] == 'broadcaster': is_broadcaster = True break if is_broadcaster: nick = '\033[7m' + nick return "\033[94m{time}\033[0m {nick} {message}".format(time=time_to_hhmmss(time), nick=nick, message=message) def print_response_messages(data, start): for comment in data['comments']: if comment['content_offset_seconds'] < start: continue if comment['source'] != 'chat': continue print(format_message(comment)) ################################################################################ if len(sys.argv) < 3 or 'TWITCH_CLIENT_ID' not in os.environ: print('Usage: TWITCH_CLIENT_ID=[client_id] {0} [video_id] [start]'.format(sys.argv[0]), file=sys.stderr) sys.exit(1) video_id = sys.argv[1] start = int(sys.argv[2]) session = requests.Session() session.headers = { 'Client-ID': os.environ['TWITCH_CLIENT_ID'], 'Accept': 'application/vnd.twitchtv.v5+json' } response = session.get('https://api.twitch.tv/v5/videos/' + video_id + '/comments?content_offset_seconds=' + str(start), timeout=10) response.raise_for_status() data = response.json() print_response_messages(data, start) cursor = None if '_next' in data: cursor = data['_next'] time.sleep(0.1) while cursor: response = session.get('https://api.twitch.tv/v5/videos/' + video_id + '/comments?cursor=' + cursor, timeout=10) response.raise_for_status() data = response.json() print_response_messages(data, start) if '_next' in data: cursor = data['_next'] time.sleep(0.1) else: cursor = None