async def _init_loginAll( self, usablePhones: list, bandPhones: list, lockPhones: list, clientCount: int ) -> typing.Union[None, typing.List[TelegramClient]]: chanDataNiUsers = self.chanDataNiUsers pickPhones = [] pickClients = [] usablePhonesLength = len(usablePhones) # 避免頻繁使用同一帳號 indexStart = random.randrange(0, usablePhonesLength) for idx in range(indexStart, usablePhonesLength + indexStart): phoneNumber = usablePhones[idx % usablePhonesLength] if novice.indexOf(bandPhones, phoneNumber) != -1 \ or novice.indexOf(lockPhones, phoneNumber) != -1: continue if chanDataNiUsers.lockPhone(phoneNumber): client = await self._login(phoneNumber) if client != None: pickPhones.append(phoneNumber) pickClients.append(client) if len(pickClients) == clientCount: return pickClients chanDataNiUsers.unlockPhones(pickPhones) for client in pickClients: await client.disconnect() return None
def pushCemeteryData(self, phoneNumber: str, err: Exception) -> None: sessionPath = self.getSessionPath(phoneNumber) if os.path.exists(sessionPath): os.remove(sessionPath) self._pushCemeteryData_chanData(phoneNumber) niUsers = self.chanData.data['niUsers'] locks = niUsers['lockList'] locksIdx = novice.indexOf(locks, phoneNumber) if locksIdx != -1: del locks[locksIdx] bands = niUsers['bandList'] bandsIdx = novice.indexOf(bands, phoneNumber) if bandsIdx != -1: del bands[bandsIdx] bandInfos = niUsers['bandInfos'] bandInfosLength = len(bandInfos) for bandInfosIdx in range(bandInfosLength): bandInfo = bandInfos[bandInfosIdx] if bandInfo['id'] == phoneNumber: del bandInfos[bandInfosIdx] break niUsers['cemetery'].append({ 'id': phoneNumber, 'message': '{} Error: {}'.format(type(err), err), }) self.chanData.store()
def _filterGuy(tgTool: TgBaseTool, mainList: typing.List[str]) -> typing.List[str]: blackGuyList = tgTool.chanData.data['blackGuy']['list'] newList = [] for peer in mainList: if novice.indexOf(blackGuyList, peer) == -1: newList.append(peer) return newList
def lockPhone(self, phoneNumber: str) -> bool: locks = self.chanData.data['niUsers']['lockList'] if novice.indexOf(locks, phoneNumber) == -1: locks.append(phoneNumber) self.pickPhones.append(phoneNumber) return True return False
async def getParticipants(self, client: TelegramClient, groupPeer: str, offset: int = 0, ynRealUser: bool = True, excludedUserList: list = [], amount: int = 200000) -> typing.Tuple[int, list]: # 每次請求用戶數 pageAmount = amount * 2 + 10 # 估值 猜想排除的用戶數 pageAmount = pageAmount if pageAmount < 100 else 100 ynHasExcludedUsers = len(excludedUserList) != 0, pickIdx = pickRealIdx = offset channelParticipantsSearch = telethon.types.ChannelParticipantsSearch( q='') ynBreak = False users = [] while len(users) < amount: participants = await client( telethon.functions.channels.GetParticipantsRequest( channel=groupPeer, filter=channelParticipantsSearch, offset=pickIdx, limit=pageAmount, hash=0)) if not participants.participants: break # No more participants left for user in participants.users: pickRealIdx += 1 # 排除 自己, 已刪除帳號, 機器人 # type(user.is_self) == type(user.deleted) == type(user.bot) == bool if ynRealUser and (user.is_self or user.deleted or user.bot): continue # 排除欲除外用戶 if ynHasExcludedUsers \ and novice.indexOf(excludedUserList, user.id) != -1: continue # 排除仿用戶 if self.lookforClientInfo(user.id) != None: continue # 可用物件有: # id, username, first_name, last_name # access_hash users.append(user) if len(users) == amount: ynBreak = True break if ynBreak: break pickIdx += pageAmount return (pickRealIdx, users)
def unlockPhones(self, *args): pickPhones = self.pickPhones if len(args) == 0: self._unlockPhones_chanData(pickPhones) pickPhones.clear() else: unlockPhones = args[0] self._unlockPhones_chanData(unlockPhones) for phoneNumber in unlockPhones: phoneIdx = novice.indexOf(pickPhones, phoneNumber) if phoneIdx != -1: del pickPhones[phoneIdx] self.chanData.store()
def pushBandData(self, phoneNumber: str, dt: datetime.datetime) -> bool: niUsers = self.chanData.data['niUsers'] bands = niUsers['bandList'] if novice.indexOf(bands, phoneNumber) == -1: bands.append(phoneNumber) niUsers['bandInfos'].append({ 'id': phoneNumber, 'bannedWaitDate': novice.dateStringify(dt), 'bannedWaitTimeMs': novice.dateTimestamp(dt) }) return False
def pushGuy(self, peer: TgTypeing.Peer, err: Exception) -> None: blackGuy = self.data['blackGuy'] blackGuyInfos = blackGuy['infos'] blackGuyList = blackGuy['list'] userId = peer.id if novice.indexOf(blackGuyList, userId) == -1: blackGuyList.append(userId) username = peer.username if username != '': blackGuyList.append(username) blackGuyInfos.append({ 'userId': peer.id, 'username': username, 'message': '{} Error: {}'.format(type(err), err), }) self.chanData.store()
async def asyncRun(args: list, _dirpy: str, _dirname: str): forwardPeersTxt = args[1] url = args[2] msg = args[3] mainGroup = novice.py_env['peers']['adChannle'] forwardPeers = forwardPeersTxt.split(',') # 用於打印日誌 runId = random.randrange(1000000, 9999999) usedClientCount = 3 latestStatus = '' try: latestStatus = '炸群進度: 初始化...' novice.logNeedle.push('(runId: {}) {}'.format(runId, latestStatus)) tgTool = TgDefaultInit(TgBaseTool, clientCount=3, papaPhone=novice.py_env['papaPhoneNumber']) await tgTool.init() except Exception as err: latestStatus += ' (失敗)' novice.logNeedle.push('(runId: {}) {}'.format(runId, latestStatus)) raise err try: latestStatus = '炸群進度: 上傳圖片...' novice.logNeedle.push('(runId: {}) {}'.format(runId, latestStatus)) messageId = await _sendFile(tgTool, mainGroup, url, msg) if messageId == -1: raise Exception( 'Use Papa send file fail. (url: {}, msg: {})'.format(url, msg)) except Exception as err: latestStatus += ' (失敗)' novice.logNeedle.push('(runId: {}) {}'.format(runId, latestStatus)) raise err try: finalPeers = _filterGuy(tgTool, forwardPeers) finalPeersLength = len(finalPeers) bandNiUserList = [] idx = 0 async for clientInfo in tgTool.iterPickClient(-1, 1, whichNiUsers=True): readableIdx = idx + 1 myId = clientInfo['id'] client = clientInfo['client'] if novice.indexOf(bandNiUserList, myId) != -1: if len(bandNiUserList) == usedClientCount: break continue if finalPeersLength <= idx: break latestStatus = '炸群進度: {}/{}'.format(readableIdx, finalPeersLength) novice.logNeedle.push('(runId: {}) ok: {}/{}'.format( runId, readableIdx, finalPeersLength)) try: forwardPeer = finalPeers[idx] await tgTool.joinGroup(client, forwardPeer) await client( telethon.functions.messages.ForwardMessagesRequest( from_peer=mainGroup, id=[messageId], to_peer=forwardPeer, random_id=[tgTool.getRandId()])) idx += 1 except telethon.errors.ChannelsTooMuchError as err: print(novice.sysTracebackException(ysHasTimestamp=True)) # 已加入了太多的渠道/超級群組。 novice.logNeedle.push( '(runId: {}) {} get ChannelsTooMuchError: wait 30 day.'. format(runId, myId)) maturityDate = novice.dateNowAfter(days=30) tgTool.chanDataNiUsers.pushBandData(myId, maturityDate) bandNiUserList.append(myId) except telethon.errors.FloodWaitError as err: print(novice.sysTracebackException(ysHasTimestamp=True)) waitTimeSec = err.seconds novice.logNeedle.push( '(runId: {}) {} get FloodWaitError: wait {} seconds.'. format(runId, myId, waitTimeSec)) # TODO 秒數待驗證 if waitTimeSec < 180: await asyncio.sleep(waitTimeSec) else: maturityDate = novice.dateNowAfter(seconds=waitTimeSec) tgTool.chanDataNiUsers.pushBandData(myId, maturityDate) bandNiUserList.append(myId) except telethon.errors.PeerFloodError as err: print(novice.sysTracebackException(ysHasTimestamp=True)) # 限制發送請求 Too many requests novice.logNeedle.push( '(runId: {}) {} get PeerFloodError: wait 1 hour.'.format( runId, myId)) # TODO 12 小時只是估計值 maturityDate = novice.dateNowAfter(hours=12) tgTool.chanDataNiUsers.pushBandData(myId, maturityDate) bandNiUserList.append(myId) except Exception as err: print(novice.sysTracebackException(ysHasTimestamp=True)) errType = type(err) novice.logNeedle.push( '(runId: {}) {} get {} Error: {} (target group: {})'. format(runId, myId, errType, err, forwardPeer)) if novice.indexOf(_invalidMessageErrorTypeList, errType) != -1: novice.logNeedle.push( 'Invalid Message Error({}): {}'.format(errType, err)) break elif novice.indexOf(_invalidPeerErrorTypeList, errType) != -1: novice.logNeedle.push('Invalid Peer Error({}): {}'.format( errType, err)) idx += 1 elif novice.indexOf(_knownErrorTypeList, errType) != -1: novice.logNeedle.push('Known Error({}): {}'.format( errType, err)) idx += 1 bandNiUserList.append(myId) else: novice.logNeedle.push('Unknown Error({}): {}'.format( type(err), err)) idx += 1 bandNiUserList.append(myId) latestStatus += ' ({})'.format('仿用戶用盡' if len(bandNiUserList) == usedClientCount else '結束') novice.logNeedle.push('(runId: {}) {}'.format(runId, latestStatus)) except Exception as err: latestStatus += ' (失敗)' novice.logNeedle.push('(runId: {}) {}'.format(runId, latestStatus)) raise err
async def asyncRun(args: list, _dirpy: str, _dirname: str): if len(args) < 5: raise ValueError( 'Usage: <phoneNumber> <loopTimes> <toGroupPeer> <forwardLink>') phoneNumber = args[1] loopTimes = int(args[2]) toGroupPeer = args[3] forwardLink = args[4] regexForwardLink = r'^https:\/\/t\.me\/([^\/]+)\/(\d+)$' matchForwardLink = re.search(regexForwardLink, forwardLink) if not matchForwardLink: raise ValueError('轉傳來源鏈結 "{}" 不如預期'.format(forwardLink)) forwardGroup = matchForwardLink.group(1) forwardMessageId = int(matchForwardLink.group(2)) tgTool = TgDefaultInit(TgSimple) print('-> 登入用戶') client = await tgTool.login(phoneNumber) myInfo = await client.get_me() print('--> I\m {} {} ({}) and my phone is +{}.'.format( str(myInfo.first_name), str(myInfo.last_name), str(myInfo.username), myInfo.phone, )) print('-> 加入聊天室') inputPeer = await client.get_entity(toGroupPeer) if type(inputPeer) != telethon.types.User: print('--> telethon.functions.channels.JoinChannelRequest') await client( telethon.functions.channels.JoinChannelRequest(channel=toGroupPeer) ) print('-> 紫爆轉傳 Hi') async for idx in tgTool.iterLoopInterval(loopTimes, 1): try: readableIdx = idx + 1 print('--> {}/{}: {} -> {}'.format(readableIdx, loopTimes, phoneNumber, toGroupPeer)) print('---> telethon.functions.messages.SendMessageRequest') await client( telethon.functions.messages.ForwardMessagesRequest( from_peer=forwardGroup, id=[forwardMessageId], to_peer=toGroupPeer, random_id=[tgTool.getRandId()])) except telethon.errors.FloodWaitError as err: print(novice.sysTracebackException(ysHasTimestamp=True)) waitTimeSec = err.seconds print('FloodWaitError: wait {} seconds. {}'.format( waitTimeSec, err)) break except telethon.errors.PeerFloodError as err: # 限制發送請求 Too many requests print(novice.sysTracebackException(ysHasTimestamp=True)) print('PeerFloodError: {}'.format(err)) break except telethon.errors.UserIsBlockedError as err: # User is blocked print(novice.sysTracebackException(ysHasTimestamp=True)) print('UserIsBlockedError: {}'.format(err)) break except Exception as err: print(novice.sysTracebackException(ysHasTimestamp=True)) errType = type(err) if novice.indexOf(_invalidMessageErrorTypeList, errType) == -1: print('Invalid Error({}): {}'.format(errType, err)) elif novice.indexOf(_knownErrorTypeList, errType) == -1: print('Known Error({}): {}'.format(errType, err)) break else: print('Unknown Error({}): {}'.format(type(err), err)) break
async def _paperSlipAction(pageId: str, innerSession: dict, data: dict): forwardPeers = data['forwardPeerList'] mainGroup = data['mainGroup'] messageId = data['messageId'] # 用於打印日誌 runId = random.randrange(1000000, 9999999) usedClientCount = 3 latestStatus = '' try: latestStatus = '炸群進度: 初始化...' novice.logNeedle.push('(runId: {}) {}'.format(runId, latestStatus)) await _paperSlipAction_send(pageId, 1, latestStatus) tgTool = TgDefaultInit( TgBaseTool, clientCount = usedClientCount, papaPhone = novice.py_env['papaPhoneNumber'] ) await tgTool.init() except Exception as err: innerSession['runing'] = False latestStatus += ' (失敗)' novice.logNeedle.push('(runId: {}) {}'.format(runId, latestStatus)) await _paperSlipAction_send(pageId, -1, latestStatus, ynError = True) return try: finalPeers = _filterGuy(tgTool, forwardPeers) finalPeersLength = len(finalPeers) bandNiUserList = [] idx = 0 async for clientInfo in tgTool.iterPickClient(-1, 1, whichNiUsers = True): readableIdx = idx + 1 myId = clientInfo['id'] client = clientInfo['client'] if novice.indexOf(bandNiUserList, myId) != -1: if len(bandNiUserList) == usedClientCount: break continue if finalPeersLength <= idx: break latestStatus = '炸群進度: {}/{}'.format(readableIdx, finalPeersLength) novice.logNeedle.push('(runId: {}) ok: {}/{}'.format( runId, readableIdx, finalPeersLength )) await _paperSlipAction_send(pageId, 1, latestStatus) try: forwardPeer = finalPeers[idx] await tgTool.joinGroup(client, forwardPeer) await client(telethon.functions.messages.ForwardMessagesRequest( from_peer = mainGroup, id = [messageId], to_peer = forwardPeer, random_id = [tgTool.getRandId()] )) idx += 1 except telethon.errors.ChannelsTooMuchError as err: # 已加入了太多的渠道/超級群組。 novice.logNeedle.push( '(runId: {}) {} get ChannelsTooMuchError: wait 30 day.'.format( runId, myId ) ) maturityDate = novice.dateNowAfter(days = 30) tgTool.chanDataNiUsers.pushBandData(myId, maturityDate) bandNiUserList.append(myId) except telethon.errors.FloodWaitError as err: waitTimeSec = err.seconds novice.logNeedle.push( '(runId: {}) {} get FloodWaitError: wait {} seconds.'.format( runId, myId, waitTimeSec ) ) # TODO 秒數待驗證 if waitTimeSec < 180: await asyncio.sleep(waitTimeSec) else: maturityDate = novice.dateNowAfter(seconds = waitTimeSec) tgTool.chanDataNiUsers.pushBandData(myId, maturityDate) bandNiUserList.append(myId) except telethon.errors.PeerFloodError as err: # 限制發送請求 Too many requests novice.logNeedle.push( '(runId: {}) {} get PeerFloodError: wait 1 hour.'.format(runId, myId) ) # TODO 12 小時只是估計值 maturityDate = novice.dateNowAfter(hours = 12) tgTool.chanDataNiUsers.pushBandData(myId, maturityDate) bandNiUserList.append(myId) except Exception as err: errType = type(err) novice.logNeedle.push( '(runId: {}) {} get {} Error: {} (target group: {})'.format( runId, myId, errType, err, forwardPeer ) ) if novice.indexOf(_invalidMessageErrorTypeList, errType) != -1: novice.logNeedle.push( 'Invalid Message Error({}): {}'.format(errType, err) ) break elif novice.indexOf(_invalidPeerErrorTypeList, errType) != -1: novice.logNeedle.push( 'Invalid Peer Error({}): {}'.format(errType, err) ) idx += 1 elif novice.indexOf(_knownErrorTypeList, errType) != -1: novice.logNeedle.push( 'Known Error({}): {}'.format(errType, err) ) idx += 1 bandNiUserList.append(myId) else: novice.logNeedle.push( 'Unknown Error({}): {}'.format(type(err), err) ) idx += 1 bandNiUserList.append(myId) latestStatus += ' ({})'.format( '仿用戶用盡' if len(bandNiUserList) == usedClientCount else '結束' ) novice.logNeedle.push('(runId: {}) {}'.format(runId, latestStatus)) await _paperSlipAction_send(pageId, 1, latestStatus) except Exception as err: latestStatus += ' (失敗)' novice.logNeedle.push('(runId: {}) {}'.format(runId, latestStatus)) await _paperSlipAction_send(pageId, -1, latestStatus, ynError = True) finally: innerSession['runing'] = False await tgTool.release()
def getUsablePhones(self) -> list: phones = self.getOwnPhones() papaPhoneIdx = novice.indexOf(phones, self._papaPhone) if papaPhoneIdx != -1: del phones[papaPhoneIdx] return phones
def _unlockPhones_chanData(self, lockPhoneList: list) -> None: locks = self.chanData.data['niUsers']['lockList'] for phoneNumber in lockPhoneList: phoneIdx = novice.indexOf(locks, phoneNumber) if phoneIdx != -1: del locks[phoneIdx]
async def _niGraph(pageId: str, receiveDatasTxt: str) -> None: try: receiveDatas = json.loads(receiveDatasTxt) # 相當於請求錯誤 if type(receiveDatas) != list: return resultDatas = [] for item in receiveDatas: resultData = {'type': ''} try: if not 'type' in item: raise KeyError('wschan: 項目缺少必要的 "type" 成員') requestMethod = item['type'] resultData['type'] = requestMethod if type(requestMethod) != str: raise TypeError('wschan: 項目 "type" 成員的類型應為字串') matchWsMethod = re.search(_regexWsMethod, requestMethod) if matchWsMethod == None: raise ValueError('wschan: 項目 "type" 成員表示式格式錯誤') fileName = matchWsMethod.group(1) methodName = matchWsMethod.group(2) if novice.indexOf(_wsChannelList, fileName) == -1: raise KeyError('wschan: 要求的方法文件不存在') pyImportPath = _wsChannelDirPyImportPath + '.' + fileName # TODO 不知為 `importlib.import_module()` 何會拋出以下訊息 # Executing <Task pending name='Task-21' coro=<ASGIWebsocketConnection.handle_websocket() running at /home/bwaycer/ys/gitman/crepo/tgNiren.py/.venv/lib/python3.8/site-packages/quart/asgi.py:147> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7f72d22eb250>()] created at /usr/lib/python3.8/asyncio/base_events.py:422> cb=[_wait.<locals>._on_completion() at /usr/lib/python3.8/asyncio/tasks.py:507] created at /home/bwaycer/ys/gitman/crepo/tgNiren.py/.venv/lib/python3.8/site-packages/quart/asgi.py:110> took 0.189 seconds module = importlib.import_module(pyImportPath) if not hasattr(module, methodName): raise KeyError('wschan: 要求的方法不存在') action = getattr(importlib.import_module(pyImportPath), methodName) if 'prop' in item: result = action(pageId, item['prop']) else: result = action(pageId) if asyncio.iscoroutine(result): result = await result # 測試是否可以編譯為 JSON (若其中包含 Python 的類型則會失敗) json.dumps(result) for key in result: resultData[key] = result[key] except Exception: errInfo = novice.sysExceptionInfo() resultData['error'] = { 'name': errInfo['name'], 'message': errInfo['message'], } novice.logNeedle.push('Catch error in ws.py\n' ' resultData:\n{}\n' ' errMsg:\n{}'.format( resultData, novice.sysTracebackException())) if resultData['type'] != '': resultDatas.append(resultData) await quart.websocket.send(json.dumps(resultDatas)) except Exception as err: raise err