diff --git a/pytchat/processors/compatible/processor.py b/pytchat/processors/compatible/processor.py index 9b799d3..23c5ef0 100644 --- a/pytchat/processors/compatible/processor.py +++ b/pytchat/processors/compatible/processor.py @@ -4,17 +4,19 @@ from .renderer.textmessage import LiveChatTextMessageRenderer from .renderer.paidmessage import LiveChatPaidMessageRenderer from .renderer.paidsticker import LiveChatPaidStickerRenderer from .renderer.legacypaid import LiveChatLegacyPaidMessageRenderer +from .renderer.membership import LiveChatMembershipItemRenderer from .. chat_processor import ChatProcessor from ... import config logger = config.logger(__name__) + class CompatibleProcessor(ChatProcessor): def process(self, chat_components: list): chatlist = [] timeout = 0 - ret={} + ret = {} ret["kind"] = "youtube#liveChatMessageListResponse" ret["etag"] = "" ret["nextPageToken"] = "" @@ -23,20 +25,24 @@ class CompatibleProcessor(ChatProcessor): for chat_component in chat_components: timeout += chat_component.get('timeout', 0) chatdata = chat_component.get('chatdata') - - if chatdata is None: break + + if chatdata is None: + break for action in chatdata: - if action is None: continue - if action.get('addChatItemAction') is None: continue - if action['addChatItemAction'].get('item') is None: continue + if action is None: + continue + if action.get('addChatItemAction') is None: + continue + if action['addChatItemAction'].get('item') is None: + continue chat = self.parse(action) if chat: chatlist.append(chat) ret["pollingIntervalMillis"] = int(timeout*1000) - ret["pageInfo"]={ - "totalResults":len(chatlist), - "resultsPerPage":len(chatlist), + ret["pageInfo"] = { + "totalResults": len(chatlist), + "resultsPerPage": len(chatlist), } ret["items"] = chatlist @@ -47,8 +53,9 @@ class CompatibleProcessor(ChatProcessor): action = sitem.get("addChatItemAction") if action: item = action.get("item") - if item is None: return None - rd={} + if item is None: + return None + rd = {} try: renderer = self.get_renderer(item) if renderer == None: @@ -57,25 +64,26 @@ class CompatibleProcessor(ChatProcessor): rd["kind"] = "youtube#liveChatMessage" rd["etag"] = "" rd["id"] = 'LCC.' + renderer.get_id() - rd["snippet"] = renderer.get_snippet() + rd["snippet"] = renderer.get_snippet() rd["authorDetails"] = renderer.get_authordetails() - except (KeyError,TypeError,AttributeError) as e: + except (KeyError, TypeError, AttributeError) as e: logger.error(f"Error: {str(type(e))}-{str(e)}") logger.error(f"item: {sitem}") return None - - return rd + + return rd def get_renderer(self, item): if item.get("liveChatTextMessageRenderer"): renderer = LiveChatTextMessageRenderer(item) elif item.get("liveChatPaidMessageRenderer"): renderer = LiveChatPaidMessageRenderer(item) - elif item.get( "liveChatPaidStickerRenderer"): + elif item.get("liveChatPaidStickerRenderer"): renderer = LiveChatPaidStickerRenderer(item) elif item.get("liveChatLegacyPaidMessageRenderer"): renderer = LiveChatLegacyPaidMessageRenderer(item) + elif item.get("liveChatMembershipItemRenderer"): + renderer = LiveChatMembershipItemRenderer(item) else: renderer = None return renderer - diff --git a/pytchat/processors/compatible/renderer/membership.py b/pytchat/processors/compatible/renderer/membership.py new file mode 100644 index 0000000..b6b8fe4 --- /dev/null +++ b/pytchat/processors/compatible/renderer/membership.py @@ -0,0 +1,40 @@ +from .base import BaseRenderer + + +class LiveChatMembershipItemRenderer(BaseRenderer): + def __init__(self, item): + super().__init__(item, "newSponsorEvent") + + def get_snippet(self): + message = self.get_message(self.renderer) + return { + "type": self.chattype, + "liveChatId": "", + "authorChannelId": self.renderer.get("authorExternalChannelId"), + "publishedAt": self.get_publishedat(self.renderer.get("timestampUsec", 0)), + "hasDisplayContent": True, + "displayMessage": message, + + } + + def get_authordetails(self): + authorExternalChannelId = self.renderer.get("authorExternalChannelId") + # parse subscriber type + isVerified, isChatOwner, _, isChatModerator = ( + self.get_badges(self.renderer) + ) + return { + "channelId": authorExternalChannelId, + "channelUrl": "http://www.youtube.com/channel/"+authorExternalChannelId, + "displayName": self.renderer["authorName"]["simpleText"], + "profileImageUrl": self.renderer["authorPhoto"]["thumbnails"][1]["url"], + "isVerified": isVerified, + "isChatOwner": isChatOwner, + "isChatSponsor": True, + "isChatModerator": isChatModerator + } + + def get_message(self, renderer): + message = (renderer["headerSubtext"]["runs"][0]["text"] + )+' / '+(renderer["authorName"]["simpleText"]) + return message diff --git a/pytchat/processors/default/processor.py b/pytchat/processors/default/processor.py index bf91e1a..3ba14c0 100644 --- a/pytchat/processors/default/processor.py +++ b/pytchat/processors/default/processor.py @@ -4,15 +4,18 @@ from .renderer.textmessage import LiveChatTextMessageRenderer from .renderer.paidmessage import LiveChatPaidMessageRenderer from .renderer.paidsticker import LiveChatPaidStickerRenderer from .renderer.legacypaid import LiveChatLegacyPaidMessageRenderer +from .renderer.membership import LiveChatMembershipItemRenderer from .. chat_processor import ChatProcessor from ... import config + logger = config.logger(__name__) + class Chatdata: - def __init__(self,chatlist:list, timeout:float): + def __init__(self, chatlist: list, timeout: float): self.items = chatlist self.interval = timeout - + def tick(self): if self.interval == 0: time.sleep(1) @@ -25,6 +28,7 @@ class Chatdata: return await asyncio.sleep(self.interval/len(self.items)) + class DefaultProcessor(ChatProcessor): def process(self, chat_components: list): @@ -35,24 +39,27 @@ class DefaultProcessor(ChatProcessor): for component in chat_components: timeout += component.get('timeout', 0) chatdata = component.get('chatdata') - if chatdata is None: continue + if chatdata is None: + continue for action in chatdata: - if action is None: continue - if action.get('addChatItemAction') is None: continue - if action['addChatItemAction'].get('item') is None: continue + if action is None: + continue + if action.get('addChatItemAction') is None: + continue + if action['addChatItemAction'].get('item') is None: + continue chat = self._parse(action) if chat: chatlist.append(chat) return Chatdata(chatlist, float(timeout)) - def _parse(self, sitem): - action = sitem.get("addChatItemAction") if action: item = action.get("item") - if item is None: return None + if item is None: + return None try: renderer = self._get_renderer(item) if renderer == None: @@ -60,20 +67,22 @@ class DefaultProcessor(ChatProcessor): renderer.get_snippet() renderer.get_authordetails() - except (KeyError,TypeError) as e: + except (KeyError, TypeError) as e: logger.error(f"{str(type(e))}-{str(e)} sitem:{str(sitem)}") return None - return renderer + return renderer def _get_renderer(self, item): if item.get("liveChatTextMessageRenderer"): renderer = LiveChatTextMessageRenderer(item) elif item.get("liveChatPaidMessageRenderer"): renderer = LiveChatPaidMessageRenderer(item) - elif item.get( "liveChatPaidStickerRenderer"): + elif item.get("liveChatPaidStickerRenderer"): renderer = LiveChatPaidStickerRenderer(item) elif item.get("liveChatLegacyPaidMessageRenderer"): renderer = LiveChatLegacyPaidMessageRenderer(item) + elif item.get("liveChatMembershipItemRenderer"): + renderer = LiveChatMembershipItemRenderer(item) else: renderer = None - return renderer \ No newline at end of file + return renderer diff --git a/pytchat/processors/default/renderer/membership.py b/pytchat/processors/default/renderer/membership.py new file mode 100644 index 0000000..a7ac3cf --- /dev/null +++ b/pytchat/processors/default/renderer/membership.py @@ -0,0 +1,15 @@ +from .base import BaseRenderer + + +class LiveChatMembershipItemRenderer(BaseRenderer): + def __init__(self, item): + super().__init__(item, "newSponsor") + + def get_authordetails(self): + super().get_authordetails() + self.author.isChatSponsor = True + + def get_message(self, renderer): + message = (renderer["headerSubtext"]["runs"][0]["text"] + )+' / '+(renderer["authorName"]["simpleText"]) + return message diff --git a/tests/test_compatible_processor.py b/tests/test_compatible_processor.py index 49cfa9c..45aec94 100644 --- a/tests/test_compatible_processor.py +++ b/tests/test_compatible_processor.py @@ -1,10 +1,11 @@ import json import pytest -import asyncio,aiohttp +import asyncio +import aiohttp from pytchat.parser.live import Parser from pytchat.processors.compatible.processor import CompatibleProcessor from pytchat.exceptions import ( - NoLivechatRendererException,NoYtinitialdataException, + NoLivechatRendererException, NoYtinitialdataException, ResponseContextError, NoContentsException) from pytchat.processors.compatible.renderer.textmessage import LiveChatTextMessageRenderer @@ -14,6 +15,7 @@ from pytchat.processors.compatible.renderer.legacypaid import LiveChatLegacyPaid parser = Parser(is_replay=False) + def test_textmessage(mocker): '''api互換processorのテスト:通常テキストメッセージ''' processor = CompatibleProcessor() @@ -22,16 +24,16 @@ def test_textmessage(mocker): _, chatdata = parser.parse(parser.get_contents(json.loads(_json))) data = { - "video_id" : "", - "timeout" : 7, - "chatdata" : chatdata + "video_id": "", + "timeout": 7, + "chatdata": chatdata } ret = processor.process([data]) - assert ret["kind"]== "youtube#liveChatMessageListResponse" - assert ret["pollingIntervalMillis"]==data["timeout"]*1000 + assert ret["kind"] == "youtube#liveChatMessageListResponse" + assert ret["pollingIntervalMillis"] == data["timeout"]*1000 assert ret.keys() == { - "kind", "etag", "pageInfo", "nextPageToken","pollingIntervalMillis","items" + "kind", "etag", "pageInfo", "nextPageToken", "pollingIntervalMillis", "items" } assert ret["pageInfo"].keys() == { "totalResults", "resultsPerPage" @@ -48,8 +50,9 @@ def test_textmessage(mocker): assert ret["items"][0]["snippet"]["textMessageDetails"].keys() == { 'messageText' } - assert "LCC." in ret["items"][0]["id"] - assert ret["items"][0]["snippet"]["type"]=="textMessageEvent" + assert "LCC." in ret["items"][0]["id"] + assert ret["items"][0]["snippet"]["type"] == "textMessageEvent" + def test_newsponcer(mocker): '''api互換processorのテスト:メンバ新規登録''' @@ -59,22 +62,22 @@ def test_newsponcer(mocker): _, chatdata = parser.parse(parser.get_contents(json.loads(_json))) data = { - "video_id" : "", - "timeout" : 7, - "chatdata" : chatdata + "video_id": "", + "timeout": 7, + "chatdata": chatdata } ret = processor.process([data]) - assert ret["kind"]== "youtube#liveChatMessageListResponse" - assert ret["pollingIntervalMillis"]==data["timeout"]*1000 + assert ret["kind"] == "youtube#liveChatMessageListResponse" + assert ret["pollingIntervalMillis"] == data["timeout"]*1000 assert ret.keys() == { - "kind", "etag", "pageInfo", "nextPageToken","pollingIntervalMillis","items" + "kind", "etag", "pageInfo", "nextPageToken", "pollingIntervalMillis", "items" } assert ret["pageInfo"].keys() == { "totalResults", "resultsPerPage" } assert ret["items"][0].keys() == { - "kind", "etag", "id", "snippet","authorDetails" + "kind", "etag", "id", "snippet", "authorDetails" } assert ret["items"][0]["snippet"].keys() == { 'type', 'liveChatId', 'authorChannelId', 'publishedAt', 'hasDisplayContent', 'displayMessage' @@ -83,8 +86,44 @@ def test_newsponcer(mocker): assert ret["items"][0]["authorDetails"].keys() == { 'channelId', 'channelUrl', 'displayName', 'profileImageUrl', 'isVerified', 'isChatOwner', 'isChatSponsor', 'isChatModerator' } - assert "LCC." in ret["items"][0]["id"] - assert ret["items"][0]["snippet"]["type"]=="newSponsorEvent" + assert "LCC." in ret["items"][0]["id"] + assert ret["items"][0]["snippet"]["type"] == "newSponsorEvent" + + +def test_newsponcer_rev(mocker): + '''api互換processorのテスト:メンバ新規登録''' + processor = CompatibleProcessor() + + _json = _open_file("tests/testdata/compatible/newSponsor_rev.json") + + _, chatdata = parser.parse(parser.get_contents(json.loads(_json))) + data = { + "video_id": "", + "timeout": 7, + "chatdata": chatdata + } + ret = processor.process([data]) + + assert ret["kind"] == "youtube#liveChatMessageListResponse" + assert ret["pollingIntervalMillis"] == data["timeout"]*1000 + assert ret.keys() == { + "kind", "etag", "pageInfo", "nextPageToken", "pollingIntervalMillis", "items" + } + assert ret["pageInfo"].keys() == { + "totalResults", "resultsPerPage" + } + assert ret["items"][0].keys() == { + "kind", "etag", "id", "snippet", "authorDetails" + } + assert ret["items"][0]["snippet"].keys() == { + 'type', 'liveChatId', 'authorChannelId', 'publishedAt', 'hasDisplayContent', 'displayMessage' + + } + assert ret["items"][0]["authorDetails"].keys() == { + 'channelId', 'channelUrl', 'displayName', 'profileImageUrl', 'isVerified', 'isChatOwner', 'isChatSponsor', 'isChatModerator' + } + assert "LCC." in ret["items"][0]["id"] + assert ret["items"][0]["snippet"]["type"] == "newSponsorEvent" def test_superchat(mocker): @@ -95,16 +134,16 @@ def test_superchat(mocker): _, chatdata = parser.parse(parser.get_contents(json.loads(_json))) data = { - "video_id" : "", - "timeout" : 7, - "chatdata" : chatdata + "video_id": "", + "timeout": 7, + "chatdata": chatdata } ret = processor.process([data]) - assert ret["kind"]== "youtube#liveChatMessageListResponse" - assert ret["pollingIntervalMillis"]==data["timeout"]*1000 + assert ret["kind"] == "youtube#liveChatMessageListResponse" + assert ret["pollingIntervalMillis"] == data["timeout"]*1000 assert ret.keys() == { - "kind", "etag", "pageInfo", "nextPageToken","pollingIntervalMillis","items" + "kind", "etag", "pageInfo", "nextPageToken", "pollingIntervalMillis", "items" } assert ret["pageInfo"].keys() == { "totalResults", "resultsPerPage" @@ -121,8 +160,9 @@ def test_superchat(mocker): assert ret["items"][0]["snippet"]["superChatDetails"].keys() == { 'amountMicros', 'currency', 'amountDisplayString', 'tier', 'backgroundColor' } - assert "LCC." in ret["items"][0]["id"] - assert ret["items"][0]["snippet"]["type"]=="superChatEvent" + assert "LCC." in ret["items"][0]["id"] + assert ret["items"][0]["snippet"]["type"] == "superChatEvent" + def test_unregistered_currency(mocker): processor = CompatibleProcessor() @@ -132,14 +172,14 @@ def test_unregistered_currency(mocker): _, chatdata = parser.parse(parser.get_contents(json.loads(_json))) data = { - "video_id" : "", - "timeout" : 7, - "chatdata" : chatdata + "video_id": "", + "timeout": 7, + "chatdata": chatdata } ret = processor.process([data]) assert ret["items"][0]["snippet"]["superChatDetails"]["currency"] == "[UNREGISTERD]" def _open_file(path): - with open(path,mode ='r',encoding = 'utf-8') as f: + with open(path, mode='r', encoding='utf-8') as f: return f.read() diff --git a/tests/testdata/compatible/newSponsor_rev.json b/tests/testdata/compatible/newSponsor_rev.json new file mode 100644 index 0000000..1e9cad9 --- /dev/null +++ b/tests/testdata/compatible/newSponsor_rev.json @@ -0,0 +1,1823 @@ +{ + "timing": { + "info": { + "st": 100 + } + }, + "response": { + "responseContext": { + "serviceTrackingParams": [ + { + "service": "CSI", + "params": [ + { + "key": "GetLiveChat_rid", + "value": "0xcb7fb21ed3bebef5" + }, + { + "key": "c", + "value": "WEB" + }, + { + "key": "cver", + "value": "2.20191010.04.06" + }, + { + "key": "yt_li", + "value": "0" + } + ] + }, + { + "service": "GFEEDBACK", + "params": [ + { + "key": "e", + "value": "23735226,23735347,23744176,23748146,23793834,23794463,23794618,23804281,23826780,23832073,23836260,23836965,23837742,23837772,23837851,23837993,23839278,23839362,23840216,23842630,23842986,23843534,23845646,23847144,23847943,23848422,23848676,23848676,23849316,23850276,23850330,23851677,24630348,24650038,9405960,9449243,9471235" + }, + { + "key": "logged_in", + "value": "0" + } + ] + }, + { + "service": "GUIDED_HELP", + "params": [ + { + "key": "logged_in", + "value": "0" + } + ] + }, + { + "service": "ECATCHER", + "params": [ + { + "key": "client.name", + "value": "WEB" + }, + { + "key": "client.version", + "value": "2.20191009" + }, + { + "key": "innertube.build.changelist", + "value": "273830839" + }, + { + "key": "innertube.build.experiments.source_version", + "value": "274226231" + }, + { + "key": "innertube.build.label", + "value": "youtube.ytfe.innertube_20191008_4_RC2" + }, + { + "key": "innertube.build.timestamp", + "value": "1570659367" + }, + { + "key": "innertube.build.variants.checksum", + "value": "3a5ae9409679bfdefdb52058a1fd326c" + }, + { + "key": "innertube.run.job", + "value": "ytfe-innertube-replica-only.ytfe" + } + ] + } + ], + "webResponseContextExtensionData": { + "ytConfigData": { + "csn": "PvujXbH0OIazqQHXgJ64DQ", + "visitorData": "CgtaMnVPX1Q0WlNlOCi-9o_tBQ%3D%3D" + } + } + }, + "continuationContents": { + "liveChatContinuation": { + "continuations": [ + { + "invalidationContinuationData": { + "invalidationId": { + "objectSource": 1056, + "objectId": "Y2hhdH5VQ25SUVlIVG5STFNGMGNMSndNbmVkQ2d-NTIzNjc1OQ==", + "topic": "chat~UCnRQYHTnRLSF0cLJwMnedCg~5236759", + "subscribeToGcmTopics": true, + "protoCreationTimestampMs": "1571027775014" + }, + "timeoutMs": 10000, + "continuation": "0ofMyAPQARpuQ2t3U0lRb1lWVU51VWxGWlNGUnVVa3hUUmpCalRFcDNUVzVsWkVObkVnVXZiR2wyWlNvbkNoaFZRMjVTVVZsSVZHNVNURk5HTUdOTVNuZE5ibVZrUTJjU0MwMURNM2RWU2kxQ1dFNUZJQUklM0Qo9qKa0d2a5QIwADgAQAJKKQgAEAAYACAAKg5zdGF0aWNjaGVja3N1bToAQABKAFCpzNfD9prlAlgDUOS4msbxmuUCWOS4msbxmuUCaAGCAQIIAYgBAKABt4LZw_aa5QI%3D", + "clickTrackingParams": "CAEQl98BIhMI1LbVw_aa5QIV2cxMAh2AtAj8" + } + } + ], + "actions": [ + { + "addChatItemAction": { + "item": { + "liveChatMembershipItemRenderer": { + "id": "ChwKGkNJZmhpX0cxa09rQ0ZZZU13Z0VkWTg4T3h3", + "timestampUsec": "1588259010572423", + "authorExternalChannelId": "UCBQCAew-l73ccSqlmqng5dw", + "headerSubtext": { + "runs": [ + { + "text": "新規メンバー" + } + ] + }, + "authorName": { + "simpleText": "レッチン" + }, + "authorPhoto": { + "thumbnails": [ + { + "url": "https://yt3.ggpht.com/-9zBO0iAYEcs/AAAAAAAAAAI/AAAAAAAAAAA/Wk6Zj2JepbQ/s32-c-k-no-mo-rj-c0xffffff/photo.jpg", + "width": 32, + "height": 32 + }, + { + "url": "https://yt3.ggpht.com/-9zBO0iAYEcs/AAAAAAAAAAI/AAAAAAAAAAA/Wk6Zj2JepbQ/s64-c-k-no-mo-rj-c0xffffff/photo.jpg", + "width": 64, + "height": 64 + } + ] + }, + "authorBadges": [ + { + "liveChatAuthorBadgeRenderer": { + "customThumbnail": { + "thumbnails": [ + { + "url": "https://yt3.ggpht.com/euoQPnY8sGZRgttD_waP1h_8WMkxRsPXLsLACOgEoGSg-7Nq7PwHk_TVktt36azGqCjArGHk=s16-c-k" + }, + { + "url": "https://yt3.ggpht.com/euoQPnY8sGZRgttD_waP1h_8WMkxRsPXLsLACOgEoGSg-7Nq7PwHk_TVktt36azGqCjArGHk=s32-c-k" + } + ] + }, + "tooltip": "新規メンバー", + "accessibility": { + "accessibilityData": { + "label": "新規メンバー" + } + } + } + } + ], + "contextMenuEndpoint": { + "commandMetadata": { + "webCommandMetadata": { + "ignoreNavigation": true + } + }, + "liveChatItemContextMenuEndpoint": { + "params": "Q2g0S0hBb2FRMGxtYUdsZlJ6RnJUMnREUmxsbFRYZG5SV1JaT0RoUGVIY1FBQm80R2cwS0N6Z3hhWEJRZEZWamNqaFJLaWNLR0ZWRGREQmpiRWd4TWxock1TMUZhalZRV0V0SFptUlFRUklMT0RGcGNGQjBWV055T0ZFZ0FTZ0JNaG9LR0ZWRFFsRkRRV1YzTFd3M00yTmpVM0ZzYlhGdVp6VmtkdyUzRCUzRA==" + } + }, + "contextMenuAccessibility": { + "accessibilityData": { + "label": "コメントの操作" + } + } + } + } + } + } + ], + "actionPanel": { + "liveChatMessageInputRenderer": { + "inputField": { + "liveChatTextInputFieldRenderer": { + "placeholder": { + "runs": [ + { + "text": "メッセージを入力..." + } + ] + }, + "maxCharacterLimit": 200, + "emojiCharacterCount": 10 + } + }, + "sendButton": { + "buttonRenderer": { + "icon": { + "iconType": "SEND" + }, + "accessibility": { + "label": "送信" + }, + "trackingParams": "CAwQ8FsiEwjUttXD9prlAhXZzEwCHYC0CPw=" + } + }, + "pickers": [ + { + "emojiPickerRenderer": { + "id": "emoji", + "categories": [ + { + "emojiPickerCategoryRenderer": { + "categoryId": "people", + "title": { + "simpleText": "人" + }, + "emojiIds": [ + "😀", + "😁", + "😂", + "🤣", + "😃", + "😄", + "😅", + "😆", + "😉", + "😊", + "😋", + "😎", + "😍", + "😘", + "😗", + "😙", + "😚", + "☺", + "🙂", + "🤗", + "🤔", + "😐", + "😑", + "😶", + "🙄", + "😏", + "😣", + "😥", + "😮", + "🤐", + "😯", + "😪", + "😫", + "😴", + "😌", + "🤓", + "😛", + "😜", + "😝", + "🤤", + "😒", + "😓", + "😔", + "😕", + "🙃", + "🤑", + "😲", + "☹", + "🙁", + "😖", + "😞", + "😟", + "😤", + "😢", + "😭", + "😦", + "😧", + "😨", + "😩", + "😬", + "😰", + "😱", + "😳", + "😵", + "😡", + "😠", + "😇", + "🤠", + "🤡", + "🤥", + "😷", + "🤒", + "🤕", + "🤢", + "🤧", + "😈", + "👿", + "👹", + "👺", + "💀", + "👻", + "👽", + "🤖", + "💩", + "😺", + "😸", + "😹", + "😻", + "😼", + "😽", + "🙀", + "😿", + "😾", + "👦", + "👧", + "👨", + "👩", + "👴", + "👵", + "👶", + "👼", + "👮", + "🕵", + "💂", + "👷", + "👳", + "👱", + "🎅", + "🤶", + "👸", + "🤴", + "👰", + "🤵", + "🤰", + "👲", + "🙍", + "🙎", + "🙅", + "🙆", + "💁", + "🙋", + "🙇", + "🤦", + "🤷", + "💆", + "💇", + "🚶", + "🏃", + "💃", + "🕺", + "👯", + "🗣", + "👤", + "👥", + "👫", + "👬", + "👭", + "💏", + "👨‍❤️‍💋‍👨", + "👩‍❤️‍💋‍👩", + "💑", + "👨‍❤️‍👨", + "👩‍❤️‍👩", + "👪", + "👨‍👩‍👧", + "👨‍👩‍👧‍👦", + "👨‍👩‍👦‍👦", + "👨‍👩‍👧‍👧", + "👨‍👨‍👦", + "👨‍👨‍👧", + "👨‍👨‍👧‍👦", + "👨‍👨‍👦‍👦", + "👨‍👨‍👧‍👧", + "👩‍👩‍👦", + "👩‍👩‍👧", + "👩‍👩‍👧‍👦", + "👩‍👩‍👦‍👦", + "👩‍👩‍👧‍👧", + "💪", + "🤳", + "👈", + "👉", + "☝", + "👆", + "🖕", + "👇", + "✌", + "🤞", + "🖖", + "🤘", + "🤙", + "🖐", + "✋", + "👌", + "👍", + "👎", + "✊", + "👊", + "🤛", + "🤜", + "🤚", + "👋", + "👏", + "✍", + "👐", + "🙌", + "🙏", + "🤝", + "💅", + "👂", + "👃", + "👣", + "👀", + "👁", + "👅", + "👄", + "💋", + "💤", + "👓", + "🕶", + "👔", + "👕", + "👖", + "👗", + "👘", + "👙", + "👚", + "👛", + "👜", + "👝", + "🎒", + "👞", + "👟", + "👠", + "👡", + "👢", + "👑", + "👒", + "🎩", + "🎓", + "⛑", + "💄", + "💍", + "🌂", + "💼" + ] + } + }, + { + "emojiPickerCategoryRenderer": { + "categoryId": "nature", + "title": { + "simpleText": "自然" + }, + "emojiIds": [ + "🙈", + "🙉", + "🙊", + "💦", + "💨", + "🐵", + "🐒", + "🦍", + "🐶", + "🐕", + "🐩", + "🐺", + "🦊", + "🐱", + "🐈", + "🦁", + "🐯", + "🐅", + "🐆", + "🐴", + "🐎", + "🦌", + "🦄", + "🐮", + "🐂", + "🐃", + "🐄", + "🐷", + "🐖", + "🐗", + "🐽", + "🐏", + "🐑", + "🐐", + "🐪", + "🐫", + "🐘", + "🦏", + "🐭", + "🐁", + "🐀", + "🐹", + "🐰", + "🐇", + "🐿", + "🦇", + "🐻", + "🐨", + "🐼", + "🐾", + "🦃", + "🐔", + "🐓", + "🐣", + "🐤", + "🐥", + "🐦", + "🐧", + "🕊", + "🦅", + "🦆", + "🦉", + "🐸", + "🐊", + "🐢", + "🦎", + "🐍", + "🐲", + "🐉", + "🐳", + "🐋", + "🐬", + "🐟", + "🐠", + "🐡", + "🦈", + "🐙", + "🐚", + "🦀", + "🦐", + "🦑", + "🦋", + "🐌", + "🐛", + "🐜", + "🐝", + "🐞", + "🕷", + "🕸", + "🦂", + "💐", + "🌸", + "🏵", + "🌹", + "🥀", + "🌺", + "🌻", + "🌼", + "🌷", + "🌱", + "🌲", + "🌳", + "🌴", + "🌵", + "🌾", + "🌿", + "☘", + "🍀", + "🍁", + "🍂", + "🍃", + "🍄", + "🌰", + "🌍", + "🌎", + "🌏", + "🌑", + "🌒", + "🌓", + "🌔", + "🌕", + "🌖", + "🌗", + "🌘", + "🌙", + "🌚", + "🌛", + "🌜", + "☀", + "🌝", + "🌞", + "⭐", + "🌟", + "☁", + "⛅", + "⛈", + "🌤", + "🌥", + "🌦", + "🌧", + "🌨", + "🌩", + "🌪", + "🌫", + "🌬", + "☂", + "☔", + "⚡", + "❄", + "☃", + "⛄", + "☄", + "🔥", + "💧", + "🌊", + "🎃", + "🎄", + "✨", + "🎋", + "🎍" + ] + } + }, + { + "emojiPickerCategoryRenderer": { + "categoryId": "food", + "title": { + "simpleText": "食べ物" + }, + "emojiIds": [ + "🍇", + "🍈", + "🍉", + "🍊", + "🍋", + "🍌", + "🍍", + "🍎", + "🍏", + "🍐", + "🍑", + "🍒", + "🍓", + "🥝", + "🍅", + "🥑", + "🍆", + "🥔", + "🥕", + "🌽", + "🌶", + "🥒", + "🥜", + "🍞", + "🥐", + "🥖", + "🥞", + "🧀", + "🍖", + "🍗", + "🥓", + "🍔", + "🍟", + "🍕", + "🌭", + "🌮", + "🌯", + "🥙", + "🥚", + "🍳", + "🥘", + "🍲", + "🥗", + "🍿", + "🍱", + "🍘", + "🍙", + "🍚", + "🍛", + "🍜", + "🍝", + "🍠", + "🍢", + "🍣", + "🍤", + "🍥", + "🍡", + "🍦", + "🍧", + "🍨", + "🍩", + "🍪", + "🎂", + "🍰", + "🍫", + "🍬", + "🍭", + "🍮", + "🍯", + "🍼", + "🥛", + "☕", + "🍵", + "🍶", + "🍾", + "🍷", + "🍸", + "🍹", + "🍺", + "🍻", + "🥂", + "🥃", + "🍽", + "🍴", + "🥄" + ] + } + }, + { + "emojiPickerCategoryRenderer": { + "categoryId": "activities", + "title": { + "simpleText": "アクティビティ" + }, + "emojiIds": [ + "👾", + "🕴", + "🤺", + "🏇", + "⛷", + "🏂", + "🏌", + "🏄", + "🚣", + "🏊", + "⛹", + "🏋", + "🚴", + "🚵", + "🤸", + "🤼", + "🤽", + "🤾", + "🤹", + "🎪", + "🎭", + "🎨", + "🎰", + "🛀", + "🎗", + "🎟", + "🎫", + "🎖", + "🏆", + "🏅", + "🥇", + "🥈", + "🥉", + "⚽", + "⚾", + "🏀", + "🏐", + "🏈", + "🏉", + "🎾", + "🎱", + "🎳", + "🏏", + "🏑", + "🏒", + "🏓", + "🏸", + "🥊", + "🥋", + "🥅", + "🎯", + "⛳", + "⛸", + "🎣", + "🎽", + "🎿", + "🎮", + "🎲", + "🎼", + "🎤", + "🎧", + "🎷", + "🎸", + "🎹", + "🎺", + "🎻", + "🥁", + "🎬", + "🏹" + ] + } + }, + { + "emojiPickerCategoryRenderer": { + "categoryId": "travel", + "title": { + "simpleText": "旅行" + }, + "emojiIds": [ + "🏎", + "🏍", + "🗾", + "🏔", + "⛰", + "🌋", + "🗻", + "🏕", + "🏖", + "🏜", + "🏝", + "🏞", + "🏟", + "🏛", + "🏗", + "🏘", + "🏙", + "🏚", + "🏠", + "🏡", + "🏢", + "🏣", + "🏤", + "🏥", + "🏦", + "🏨", + "🏩", + "🏪", + "🏫", + "🏬", + "🏭", + "🏯", + "🏰", + "💒", + "🗼", + "🗽", + "⛪", + "🕌", + "🕍", + "⛩", + "🕋", + "⛲", + "⛺", + "🌁", + "🌃", + "🌄", + "🌅", + "🌆", + "🌇", + "🌉", + "🌌", + "🎠", + "🎡", + "🎢", + "🚂", + "🚃", + "🚄", + "🚅", + "🚆", + "🚇", + "🚈", + "🚉", + "🚊", + "🚝", + "🚞", + "🚋", + "🚌", + "🚍", + "🚎", + "🚐", + "🚑", + "🚒", + "🚓", + "🚔", + "🚕", + "🚖", + "🚗", + "🚘", + "🚙", + "🚚", + "🚛", + "🚜", + "🚲", + "🛴", + "🛵", + "🚏", + "🛣", + "🛤", + "⛽", + "🚨", + "🚥", + "🚦", + "🚧", + "⚓", + "⛵", + "🛶", + "🚤", + "🛳", + "⛴", + "🛥", + "🚢", + "✈", + "🛩", + "🛫", + "🛬", + "💺", + "🚁", + "🚟", + "🚠", + "🚡", + "🚀", + "🛰", + "🌠", + "🌈", + "🎆", + "🎇", + "🎑", + "🏁" + ] + } + }, + { + "emojiPickerCategoryRenderer": { + "categoryId": "objects", + "title": { + "simpleText": "オブジェクト" + }, + "emojiIds": [ + "☠", + "💌", + "💣", + "🕳", + "🛍", + "📿", + "💎", + "🔪", + "🏺", + "🗺", + "💈", + "🖼", + "🛎", + "🚪", + "🛌", + "🛏", + "🛋", + "🚽", + "🚿", + "🛁", + "⌛", + "⏳", + "⌚", + "⏰", + "⏱", + "⏲", + "🕰", + "🌡", + "⛱", + "🎈", + "🎉", + "🎊", + "🎎", + "🎏", + "🎐", + "🎀", + "🎁", + "🕹", + "📯", + "🎙", + "🎚", + "🎛", + "📻", + "📱", + "📲", + "☎", + "📞", + "📟", + "📠", + "🔋", + "🔌", + "💻", + "🖥", + "🖨", + "⌨", + "🖱", + "🖲", + "💽", + "💾", + "💿", + "📀", + "🎥", + "🎞", + "📽", + "📺", + "📷", + "📸", + "📹", + "📼", + "🔍", + "🔎", + "🔬", + "🔭", + "📡", + "🕯", + "💡", + "🔦", + "🏮", + "📔", + "📕", + "📖", + "📗", + "📘", + "📙", + "📚", + "📓", + "📒", + "📃", + "📜", + "📄", + "📰", + "🗞", + "📑", + "🔖", + "🏷", + "💰", + "💴", + "💵", + "💶", + "💷", + "💸", + "💳", + "✉", + "📧", + "📨", + "📩", + "📤", + "📥", + "📦", + "📫", + "📪", + "📬", + "📭", + "📮", + "🗳", + "✏", + "✒", + "🖋", + "🖊", + "🖌", + "🖍", + "📝", + "📁", + "📂", + "🗂", + "📅", + "📆", + "🗒", + "🗓", + "📇", + "📈", + "📉", + "📊", + "📋", + "📌", + "📍", + "📎", + "🖇", + "📏", + "📐", + "✂", + "🗃", + "🗄", + "🗑", + "🔒", + "🔓", + "🔏", + "🔐", + "🔑", + "🗝", + "🔨", + "⛏", + "⚒", + "🛠", + "🗡", + "⚔", + "🔫", + "🛡", + "🔧", + "🔩", + "⚙", + "🗜", + "⚗", + "⚖", + "🔗", + "⛓", + "💉", + "💊", + "🚬", + "⚰", + "⚱", + "🗿", + "🛢", + "🔮", + "🛒", + "🚩", + "🎌", + "🏴", + "🏳" + ] + } + }, + { + "emojiPickerCategoryRenderer": { + "categoryId": "symbols", + "title": { + "simpleText": "記号" + }, + "emojiIds": [ + "❤", + "💛", + "💚", + "💙", + "💜", + "💔", + "❣", + "💕", + "💞", + "💓", + "💗", + "💖", + "💘", + "💝", + "💟", + "☮", + "✝", + "☪", + "🕉", + "☸", + "✡", + "🔯", + "🕎", + "☯", + "☦", + "🛐", + "⛎", + "♈", + "♉", + "♊", + "♋", + "♌", + "♍", + "♎", + "♏", + "♐", + "♑", + "♒", + "♓", + "⚛", + "☢", + "☣", + "📴", + "📳", + "✴", + "💮", + "⛔", + "📛", + "🚫", + "❌", + "⭕", + "💢", + "♨", + "🚷", + "🚯", + "🚳", + "🚱", + "🔞", + "📵", + "❗", + "❕", + "❓", + "❔", + "‼", + "⁉", + "💯", + "🔅", + "🔆", + "🔱", + "⚜", + "〽", + "⚠", + "🚸", + "🔰", + "♻", + "❇", + "✳", + "❎", + "✅", + "💠", + "🌀", + "➿", + "🌐", + "🏧", + "🛂", + "🛃", + "🛄", + "🛅", + "♿", + "🚭", + "🚾", + "🚰", + "🚹", + "🚺", + "🚼", + "🚻", + "🚮", + "🎦", + "📶", + "▶", + "⏸", + "⏯", + "⏹", + "⏺", + "⏭", + "⏮", + "⏩", + "⏪", + "🔀", + "🔁", + "🔂", + "◀", + "🔼", + "🔽", + "⏫", + "⏬", + "➡", + "⬅", + "⬆", + "⬇", + "↗", + "↘", + "↙", + "↖", + "↕", + "↔", + "🔄", + "↪", + "↩", + "⤴", + "⤵", + "🔣", + "🎵", + "🎶", + "〰", + "➰", + "✔", + "🔃", + "➕", + "➖", + "➗", + "✖", + "💲", + "💱", + "☑", + "🔘", + "⚪", + "⚫", + "🔴", + "🔵", + "🔸", + "🔹", + "🔶", + "🔷", + "🔺", + "▪", + "▫", + "⬛", + "⬜", + "🔻", + "◼", + "◻", + "◾", + "◽", + "🔲", + "🔳", + "🔈", + "🔉", + "🔊", + "🔇", + "📣", + "📢", + "🔔", + "🔕", + "🃏", + "🀄", + "♠", + "♣", + "♥", + "♦", + "🎴", + "💭", + "🗯", + "💬", + "🕐", + "🕑", + "🕒", + "🕓", + "🕔", + "🕕", + "🕖", + "🕗", + "🕘", + "🕙", + "🕚", + "🕛", + "🕜", + "🕝", + "🕞", + "🕟", + "🕠", + "🕡", + "🕢", + "🕣", + "🕤", + "🕥", + "🕦", + "🕧", + "👁‍🗨", + "🗨", + "⏏" + ] + } + } + ], + "categoryButtons": [ + { + "emojiPickerCategoryButtonRenderer": { + "categoryId": "people", + "icon": { + "iconType": "EMOJI_PEOPLE" + }, + "tooltip": "人", + "accessibility": { + "accessibilityData": { + "label": "人" + } + } + } + }, + { + "emojiPickerCategoryButtonRenderer": { + "categoryId": "nature", + "icon": { + "iconType": "EMOJI_NATURE" + }, + "tooltip": "自然", + "accessibility": { + "accessibilityData": { + "label": "自然" + } + } + } + }, + { + "emojiPickerCategoryButtonRenderer": { + "categoryId": "food", + "icon": { + "iconType": "EMOJI_FOOD" + }, + "tooltip": "食べ物", + "accessibility": { + "accessibilityData": { + "label": "食べ物" + } + } + } + }, + { + "emojiPickerCategoryButtonRenderer": { + "categoryId": "activities", + "icon": { + "iconType": "EMOJI_ACTIVITIES" + }, + "tooltip": "アクティビティ", + "accessibility": { + "accessibilityData": { + "label": "アクティビティ" + } + } + } + }, + { + "emojiPickerCategoryButtonRenderer": { + "categoryId": "travel", + "icon": { + "iconType": "EMOJI_TRAVEL" + }, + "tooltip": "旅行", + "accessibility": { + "accessibilityData": { + "label": "旅行" + } + } + } + }, + { + "emojiPickerCategoryButtonRenderer": { + "categoryId": "objects", + "icon": { + "iconType": "EMOJI_OBJECTS" + }, + "tooltip": "オブジェクト", + "accessibility": { + "accessibilityData": { + "label": "オブジェクト" + } + } + } + }, + { + "emojiPickerCategoryButtonRenderer": { + "categoryId": "symbols", + "icon": { + "iconType": "EMOJI_SYMBOLS" + }, + "tooltip": "記号", + "accessibility": { + "accessibilityData": { + "label": "記号" + } + } + } + } + ], + "searchPlaceholderText": { + "runs": [ + { + "text": "絵文字を検索" + } + ] + }, + "searchNoResultsText": { + "runs": [ + { + "text": "絵文字が見つかりませんでした" + } + ] + }, + "pickSkinToneText": { + "runs": [ + { + "text": "絵文字の肌の色を選択" + } + ] + }, + "trackingParams": "CAsQsrQCGAUiEwjUttXD9prlAhXZzEwCHYC0CPw=", + "clearSearchLabel": "検索をクリア", + "skinToneGenericLabel": "汎用的な肌の色", + "skinToneLightLabel": "明るい肌の色", + "skinToneMediumLightLabel": "やや明るい肌の色", + "skinToneMediumLabel": "中間的な明るさの肌の色", + "skinToneMediumDarkLabel": "やや濃い肌の色", + "skinToneDarkLabel": "濃い肌の色" + } + } + ], + "pickerButtons": [ + { + "liveChatIconToggleButtonRenderer": { + "targetId": "emoji", + "icon": { + "iconType": "EMOJI" + }, + "tooltip": "絵文字", + "accessibility": { + "accessibilityData": { + "label": "絵文字" + } + }, + "toggledIcon": { + "iconType": "KEYBOARD" + }, + "trackingParams": "CAoQtIkEGAYiEwjUttXD9prlAhXZzEwCHYC0CPw=" + } + } + ], + "interactionMessage": { + "messageRenderer": { + "trackingParams": "CAgQljsiEwjUttXD9prlAhXZzEwCHYC0CPw=", + "button": { + "buttonRenderer": { + "style": "STYLE_DARK", + "size": "SIZE_DEFAULT", + "isDisabled": false, + "text": { + "simpleText": "ログインしてチャットを始める" + }, + "navigationEndpoint": { + "clickTrackingParams": "CAkQ8FsiEwjUttXD9prlAhXZzEwCHYC0CPw=", + "commandMetadata": { + "webCommandMetadata": { + "url": "https://accounts.google.com/ServiceLogin?service=youtube\u0026uilel=3\u0026passive=true\u0026continue=https%3A%2F%2Fwww.youtube.com%2Fsignin%3Faction_handle_signin%3Dtrue%26app%3Ddesktop%26hl%3Dja%26next%3Dhttps%253A%252F%252Fwww.youtube.com%252Flive_chat%253Fv%253DMC3wUJ-BXNE%2526is_popout%253D1\u0026hl=ja", + "rootVe": 83769 + } + }, + "signInEndpoint": { + "nextEndpoint": { + "clickTrackingParams": "CAkQ8FsiEwjUttXD9prlAhXZzEwCHYC0CPw=", + "commandMetadata": { + "webCommandMetadata": { + "url": "https://www.youtube.com/live_chat?v=MC3wUJ-BXNE\u0026is_popout=1", + "rootVe": 83769 + } + }, + "urlEndpoint": { + "url": "https://www.youtube.com/live_chat?v=MC3wUJ-BXNE\u0026is_popout=1" + } + } + } + }, + "accessibility": { + "label": "ログインしてチャットを始める" + }, + "trackingParams": "CAkQ8FsiEwjUttXD9prlAhXZzEwCHYC0CPw=" + } + }, + "subtext": { + "messageSubtextRenderer": { + "text": { + "simpleText": "送信したすべてのメッセージが公開されます" + } + } + } + } + } + } + }, + "itemList": { + "liveChatItemListRenderer": { + "maxItemsToDisplay": 250, + "moreCommentsBelowButton": { + "buttonRenderer": { + "style": "STYLE_PRIMARY", + "icon": { + "iconType": "DOWN_ARROW" + }, + "trackingParams": "CAcQ8FsiEwjUttXD9prlAhXZzEwCHYC0CPw=", + "accessibilityData": { + "accessibilityData": { + "label": "さらに下のコメントを表示" + } + } + } + }, + "enablePauseChatKeyboardShortcuts": false + } + }, + "header": { + "liveChatHeaderRenderer": { + "overflowMenu": { + "menuRenderer": { + "items": [ + { + "menuServiceItemRenderer": { + "text": { + "runs": [ + { + "text": "参加者" + } + ] + }, + "icon": { + "iconType": "PERSON" + }, + "serviceEndpoint": { + "showLiveChatParticipantsEndpoint": { + "hack": true + } + }, + "trackingParams": "CAEQl98BIhMI1LbVw_aa5QIV2cxMAh2AtAj8" + } + }, + { + "menuServiceItemRenderer": { + "text": { + "runs": [ + { + "text": "タイムスタンプ表示切替" + } + ] + }, + "icon": { + "iconType": "ACCESS_TIME" + }, + "serviceEndpoint": { + "toggleLiveChatTimestampsEndpoint": { + "hack": true + } + }, + "trackingParams": "CAEQl98BIhMI1LbVw_aa5QIV2cxMAh2AtAj8" + } + }, + { + "menuNavigationItemRenderer": { + "text": { + "runs": [ + { + "text": "フィードバックを送信" + } + ] + }, + "icon": { + "iconType": "FEEDBACK" + }, + "navigationEndpoint": { + "clickTrackingParams": "CAEQl98BIhMI1LbVw_aa5QIV2cxMAh2AtAj8", + "commandMetadata": { + "webCommandMetadata": { + "ignoreNavigation": true + } + }, + "userFeedbackEndpoint": { + "hack": true, + "bucketIdentifier": "live_chat" + } + }, + "trackingParams": "CAEQl98BIhMI1LbVw_aa5QIV2cxMAh2AtAj8" + } + } + ], + "trackingParams": "CAEQl98BIhMI1LbVw_aa5QIV2cxMAh2AtAj8", + "accessibility": { + "accessibilityData": { + "label": "その他のオプション" + } + } + } + }, + "collapseButton": { + "buttonRenderer": { + "style": "STYLE_DEFAULT", + "size": "SIZE_DEFAULT", + "isDisabled": false, + "accessibility": { + "label": "チャットの展開 / 折りたたみ" + }, + "trackingParams": "CAYQ8FsiEwjUttXD9prlAhXZzEwCHYC0CPw=" + } + }, + "viewSelector": { + "sortFilterSubMenuRenderer": { + "subMenuItems": [ + { + "title": "上位チャット", + "selected": false, + "continuation": { + "reloadContinuationData": { + "continuation": "0ofMyAOBAhrKAUNrd1NJUW9ZVlVOdVVsRlpTRlJ1VWt4VFJqQmpURXAzVFc1bFpFTm5FZ1V2YkdsMlpTb25DaGhWUTI1U1VWbElWRzVTVEZOR01HTk1TbmROYm1Wa1EyY1NDMDFETTNkVlNpMUNXRTVGR2tPcXVjRzlBVDBLTzJoMGRIQnpPaTh2ZDNkM0xubHZkWFIxWW1VdVkyOXRMMnhwZG1WZlkyaGhkRDkyUFUxRE0zZFZTaTFDV0U1RkptbHpYM0J2Y0c5MWREMHhJQUklM0QwAUopCAAQABgAIAAqDnN0YXRpY2NoZWNrc3VtOgBAAEoAUKnM18P2muUCWANoBIIBAggE", + "clickTrackingParams": "CAUQxqYCIhMI1LbVw_aa5QIV2cxMAh2AtAj8" + } + }, + "accessibility": { + "accessibilityData": { + "label": "上位チャット" + } + }, + "subtitle": "一部のメッセージ(不適切な可能性があるものなど)を非表示にします" + }, + { + "title": "チャット", + "selected": true, + "continuation": { + "reloadContinuationData": { + "continuation": "0ofMyAOBAhrKAUNrd1NJUW9ZVlVOdVVsRlpTRlJ1VWt4VFJqQmpURXAzVFc1bFpFTm5FZ1V2YkdsMlpTb25DaGhWUTI1U1VWbElWRzVTVEZOR01HTk1TbmROYm1Wa1EyY1NDMDFETTNkVlNpMUNXRTVGR2tPcXVjRzlBVDBLTzJoMGRIQnpPaTh2ZDNkM0xubHZkWFIxWW1VdVkyOXRMMnhwZG1WZlkyaGhkRDkyUFUxRE0zZFZTaTFDV0U1RkptbHpYM0J2Y0c5MWREMHhJQUklM0QwAUopCAAQABgAIAAqDnN0YXRpY2NoZWNrc3VtOgBAAEoAUKnM18P2muUCWANoAYIBAggB", + "clickTrackingParams": "CAQQxqYCIhMI1LbVw_aa5QIV2cxMAh2AtAj8" + } + }, + "accessibility": { + "accessibilityData": { + "label": "チャット" + } + }, + "subtitle": "すべてのメッセージが表示されます" + } + ], + "accessibility": { + "accessibilityData": { + "label": "チャットのモードの選択" + } + }, + "trackingParams": "CAMQgdoEIhMI1LbVw_aa5QIV2cxMAh2AtAj8" + } + } + } + }, + "ticker": { + "liveChatTickerRenderer": { + "sentinel": true + } + }, + "trackingParams": "CAEQl98BIhMI1LbVw_aa5QIV2cxMAh2AtAj8", + "participantsList": { + "liveChatParticipantsListRenderer": { + "title": { + "runs": [ + { + "text": "参加者" + } + ] + }, + "backButton": { + "buttonRenderer": { + "icon": { + "iconType": "BACK" + }, + "trackingParams": "CAIQ8FsiEwjUttXD9prlAhXZzEwCHYC0CPw=", + "accessibilityData": { + "accessibilityData": { + "label": "戻る" + } + } + } + }, + "participants": [ + { + "liveChatParticipantRenderer": { + "authorName": { + "simpleText": "相羽ういは〖Aiba Uiha〗にじさんじ所属" + }, + "authorPhoto": { + "thumbnails": [ + { + "url": "https://yt3.ggpht.com/-oKuaFgPf7a8/AAAAAAAAAAI/AAAAAAAAAAA/6oDCrDvXdsc/s32-c-k-no-mo-rj-c0xffffff/photo.jpg", + "width": 32, + "height": 32 + }, + { + "url": "https://yt3.ggpht.com/-oKuaFgPf7a8/AAAAAAAAAAI/AAAAAAAAAAA/6oDCrDvXdsc/s64-c-k-no-mo-rj-c0xffffff/photo.jpg", + "width": 64, + "height": 64 + } + ] + }, + "authorBadges": [ + { + "liveChatAuthorBadgeRenderer": { + "icon": { + "iconType": "OWNER" + }, + "tooltip": "所有者", + "accessibility": { + "accessibilityData": { + "label": "所有者" + } + } + } + } + ] + } + } + ] + } + }, + "clientMessages": { + "reconnectMessage": { + "runs": [ + { + "text": "チャットが切断されました。再接続するまでしばらくお待ちください。" + } + ] + }, + "unableToReconnectMessage": { + "runs": [ + { + "text": "チャットに接続できません。しばらくしてからもう一度お試しください。" + } + ] + }, + "fatalError": { + "runs": [ + { + "text": "チャットに接続できません。しばらくしてからもう一度お試しください。" + } + ] + }, + "reconnectedMessage": { + "runs": [ + { + "text": "接続しました。" + } + ] + }, + "genericError": { + "runs": [ + { + "text": "エラーが発生しました。もう一度お試しください。" + } + ] + } + } + } + }, + "trackingParams": "CAAQ0b4BIhMI1LbVw_aa5QIV2cxMAh2AtAj8" + }, + "url": "\/live_chat\/get_live_chat?continuation=0ofMyAOBAhrKAUNrd1NJUW9ZVlVOdVVsRlpTRlJ1VWt4VFJqQmpURXAzVFc1bFpFTm5FZ1V2YkdsMlpTb25DaGhWUTI1U1VWbElWRzVTVEZOR01HTk1TbmROYm1Wa1EyY1NDMDFETTNkVlNpMUNXRTVGR2tPcXVjRzlBVDBLTzJoMGRIQnpPaTh2ZDNkM0xubHZkWFIxWW1VdVkyOXRMMnhwZG1WZlkyaGhkRDkyUFUxRE0zZFZTaTFDV0U1RkptbHpYM0J2Y0c5MWREMHhJQUklM0QwAUopCAAQABgAIAAqDnN0YXRpY2NoZWNrc3VtOgBAAEoAUPOaw8P2muUCWANoAYIBAggB", + "csn": "PvujXbH0OIazqQHXgJ64DQ", + "endpoint": { + "commandMetadata": { + "webCommandMetadata": { + "url": "/live_chat/get_live_chat?continuation=0ofMyAOBAhrKAUNrd1NJUW9ZVlVOdVVsRlpTRlJ1VWt4VFJqQmpURXAzVFc1bFpFTm5FZ1V2YkdsMlpTb25DaGhWUTI1U1VWbElWRzVTVEZOR01HTk1TbmROYm1Wa1EyY1NDMDFETTNkVlNpMUNXRTVGR2tPcXVjRzlBVDBLTzJoMGRIQnpPaTh2ZDNkM0xubHZkWFIxWW1VdVkyOXRMMnhwZG1WZlkyaGhkRDkyUFUxRE0zZFZTaTFDV0U1RkptbHpYM0J2Y0c5MWREMHhJQUklM0QwAUopCAAQABgAIAAqDnN0YXRpY2NoZWNrc3VtOgBAAEoAUPOaw8P2muUCWANoAYIBAggB", + "rootVe": 83769 + } + }, + "urlEndpoint": { + "url": "/live_chat/get_live_chat?continuation=0ofMyAOBAhrKAUNrd1NJUW9ZVlVOdVVsRlpTRlJ1VWt4VFJqQmpURXAzVFc1bFpFTm5FZ1V2YkdsMlpTb25DaGhWUTI1U1VWbElWRzVTVEZOR01HTk1TbmROYm1Wa1EyY1NDMDFETTNkVlNpMUNXRTVGR2tPcXVjRzlBVDBLTzJoMGRIQnpPaTh2ZDNkM0xubHZkWFIxWW1VdVkyOXRMMnhwZG1WZlkyaGhkRDkyUFUxRE0zZFZTaTFDV0U1RkptbHpYM0J2Y0c5MWREMHhJQUklM0QwAUopCAAQABgAIAAqDnN0YXRpY2NoZWNrc3VtOgBAAEoAUPOaw8P2muUCWANoAYIBAggB" + } + }, + "xsrf_token": "QUFFLUhqbTBwS2VJSERSSGl3cG5BSFhqMERYeGxrRk1sd3xBQ3Jtc0tudnVRN3Z4cDJlcWpMelVTTjZvVE5lX010aWtfRGNYeGJvUTlNSHNtRHcwVDNLLXpfZlhyTFJIZ1NXVFBnYjA3dHJQRGViaDFMTWloelhwN3RzV3JNa2hhUlJIa09nb0JWaURISDJIUjlFVlRUT3ppemsySjExbzdEM1hudG1abHV6b29KellpaHdsVzROSXE1U0ZSMzhWSzhjQWc=" +} \ No newline at end of file