async def connect(self) -> Optional[bool]: if self._transport is not None: raise ConnectionError('connection already exists') now: datetime = utils.now() if now - self.lastConnectAttempt < timedelta(seconds=1): return None self.lastConnectAttempt = now try: reader: asyncio.StreamReader writer: asyncio.StreamWriter reader, writer = await asyncio.open_connection(*self.address) print(f'{utils.now()} {self.name} Connected {self.server}') await self.login(writer) self._reader = reader self._writer = writer self._transport = writer.transport now = utils.now() self.lastSentPing = now self.lastPing = now join.connected(self) return True except ConnectionError: return False
def managedAccounts(self, accountsList): msg = { 'type': 'managedAccounts', 'accountsList': accountsList, 'ts': now() } self.msgs.put(msg)
def _log_write(self, command: IrcMessage, *, channel: 'Optional[data.Channel]' = None, whisper: 'Optional[WhisperMessage]' = None, timestamp: Optional[datetime] = None) -> None: timestamp = timestamp or utils.now() if command.command == 'PASS': command = IrcMessage(command='PASS') files: List[str] = [] logs: List[str] = [] files.append(f'{bot.config.botnick}-{self.name}.log') logs.append('> ' + str(command)) file: str log: str if whisper and channel: for file, log in zip(files, logs): utils.logIrcMessage(file, log, timestamp) raise ValueError() if whisper: files.append(f'@{whisper.nick}@whisper.log') logs.append(f'{bot.config.botnick}: {whisper.message}') files.append(f'{bot.config.botnick}-All Whisper.log') logs.append( f'{bot.config.botnick} -> {whisper.nick}: {whisper.message}') files.append(f'{bot.config.botnick}-Raw Whisper.log') logs.append(f'> {command}') if channel: files.append(f'{channel.ircChannel}#full.log') logs.append(f'> {command}') if command.command == 'PRIVMSG': files.append(f'{channel.ircChannel}#msg.log') logs.append(f'{bot.config.botnick}: {command.params.trailing}') for file, log in zip(files, logs): utils.logIrcMessage(file, log, timestamp)
def run_tasks() -> None: timestamp: datetime = utils.now() task: Task for task in _tasks[:]: if timestamp >= task.timestamp + task.interval: asyncio.ensure_future(_run_task(task.task, timestamp)) task.timestamp = timestamp
async def data(*args, **kwargs) -> Any: if key not in bot.globals.globalSessionData: d: defaultdict = defaultdict(lambda: (datetime.min, default)) bot.globals.globalSessionData[key] = d lastTime: datetime value: Any kargs: _ArgsKey kargs = args, tuple(kwargs.items()) lastTime, value = bot.globals.globalSessionData[key][kargs] if utils.now() - lastTime >= duration: with suppress(*excepts): value = await func(*args, **kwargs) data: dict = bot.globals.globalSessionData[key] data[kargs] = utils.now(), value return value
def historicalData(self, reqId, date, open, high, low, close, volume, count, WAP, hasGaps): msg = {'type': 'historicalData', 'reqId': reqId, 'date': date, 'open': open, 'high': high, 'low': low, 'close': close, 'volume': volume, 'count': count, 'WAP': WAP, 'hasGaps': hasGaps, 'ts': now()} self.msgs.put(msg)
def orderStatus(self, orderId, status, filled, remaining, avgFillPrice, permId, parentId, lastFillPrice, clientId, whyHeId): msg = {'type': 'orderStatus', 'orderId': orderId, 'status': status, 'filled': filled, 'remaining': remaining, 'avgFillPrice': avgFillPrice, 'permId': permId, 'parentId': parentId, 'lastFillPrice': lastFillPrice, 'clientId': clientId, 'whyHeId': whyHeId, 'ts': now()} self.msgs.put(msg)
def cleanOldTimestamps(self) -> None: timestamp = utils.now() msgDuration = timedelta(seconds=bot.config.messageSpan) self._chatSent = [t for t in self._chatSent if timestamp - t <= msgDuration] msgDuration = timedelta(seconds=bot.config.whiperSpan) self._whisperSent = [t for t in self._whisperSent if timestamp - t <= msgDuration]
def execDetails(self, reqId, contract, execution): msg = { 'type': 'execDetails', 'reqId': reqId, 'contract': contract, 'execution': execution, 'ts': now() } self.msgs.put(msg)
def tickPrice(self, tickerId, field, px, _canAutoExecute): msg = { 'type': 'tickPrice', 'symbol': self.inst_map[tickerId]['symbol'], 'field': field, 'price': px, 'ts': now() } self.msgs.put(msg)
def tickSize(self, tickerId, field, sz): msg = { 'type': 'tickSize', 'symbol': self.inst_map[tickerId]['symbol'], 'field': field, 'size': sz, 'ts': now() } self.msgs.put(msg)
def error_1(self, id=None, errorCode=None, errorMsg=None): msg = { 'type': 'error_1', 'id': id, 'errorCode': errorCode, 'errorMsg': errorMsg, 'ts': now() } self.msgs.put(msg)
def tickString(self, tickerId, tickType, value): msg = { 'type': 'tickString', 'symbol': self.inst_map[tickerId]['symbol'], 'tickType': tickType, 'value': value, 'ts': now() } self.msgs.put(msg)
async def getAutoRepeatToSend(self, timestamp: Optional[datetime]=None ) -> 'AsyncIterator[data.AutoRepeatMessage]': now: datetime = timestamp or utils.now() repeats: List[data.RepeatData] = await self._getAutoRepeats() repeat: data.RepeatData for repeat in repeats: if repeat.last + timedelta(minutes=repeat.duration) > now: continue yield data.AutoRepeatMessage(repeat.broadcaster, repeat.name, repeat.message)
def position(self, account, contract, pos, avgCost): msg = { 'type': 'position', 'account': account, 'contract': contract, 'pos': pos, 'avgCost': avgCost, 'ts': now() } self.msgs.put(msg)
def openOrder(self, orderId, contract, order, state): msg = { 'type': 'openOrder', 'orderId': orderId, 'contract': contract, 'order': order, 'state': state, 'ts': now() } self.msgs.put(msg)
def send_ping(self) -> None: now = utils.now() sinceLastSend: timedelta = now - self.lastSentPing sinceLast: timedelta = now - self.lastPing if sinceLastSend >= timedelta(minutes=1): self.queue_write(IrcMessage(None, None, 'PING', IrcMessageParams(bot.config.botnick)), prepend=True) self.lastSentPing = now elif sinceLast >= timedelta(minutes=1, seconds=15): raise ConnectionError()
def disconnect(self) -> None: if self._transport is None: raise ConnectionError() self._transport.close() join.disconnected(self) self._transport = None self._reader = None self._writer = None self.lastConnectAttempt = utils.now() self.lastSentPing = datetime.max self.lastPing = datetime.max print(f'{utils.now()} {self.name} Disconnected {self.server}')
async def timeout_user(dataCache: CacheStore, chat: 'data.Channel', user: str, module: str, base_level: int=0, message: Optional[str]=None, reason: Optional[str]=None): properties: List[str] defaults: Dict[str, int] chatProp: Mapping[str, int] timeouts: List[int] properties = ['timeoutLength0', 'timeoutLength1', 'timeoutLength2'] defaults = {'timeoutLength0': bot.config.moderatorDefaultTimeout[0], 'timeoutLength1': bot.config.moderatorDefaultTimeout[1], 'timeoutLength2': bot.config.moderatorDefaultTimeout[2], } chatProp = await dataCache.getChatProperties(chat.channel, properties, defaults, int) timeouts = [chatProp['timeoutLength0'], chatProp['timeoutLength1'], chatProp['timeoutLength2'], ] if 'timeouts' not in chat.sessionData: chat.sessionData['timeouts'] = defaultdict( lambda: defaultdict( lambda: (datetime.min, 0))) timestamp: datetime = utils.now() duration: timedelta = timedelta(seconds=bot.config.warningDuration) level: int if timestamp - chat.sessionData['timeouts'][module][user][0] >= duration: level = min(max(base_level, 0), 2) else: prevLevel: int = chat.sessionData['timeouts'][module][user][1] level = min(max(base_level + 1, prevLevel + 1, 0), 2) chat.sessionData['timeouts'][module][user] = timestamp, level length: int = timeouts[level] theReason: str = reason or '' if length: chat.send(f'.timeout {user} {length} {theReason}', 0) else: chat.send(f'.ban {user} {theReason}', 0) db: DatabaseTimeout async with DatabaseTimeout.acquire() as db: await db.recordTimeout(chat.channel, user, None, module, level, length, message, reason)
def realtimeBar(self, reqId, time, open, high, low, close, volume, wap, count): msg = { 'type': 'realtimeBar', 'reqId': reqId, 'time': time, 'open': open, 'high': high, 'low': low, 'close': close, 'volume': volume, 'count': count, 'WAP': WAP, 'ts': now() } self.msgs.put(msg)
def orderStatus(self, orderId, status, filled, remaining, avgFillPrice, permId, parentId, lastFillPrice, clientId, whyHeId): msg = { 'type': 'orderStatus', 'orderId': orderId, 'status': status, 'filled': filled, 'remaining': remaining, 'avgFillPrice': avgFillPrice, 'permId': permId, 'parentId': parentId, 'lastFillPrice': lastFillPrice, 'clientId': clientId, 'whyHeId': whyHeId, 'ts': now() } self.msgs.put(msg)
def historicalData(self, reqId, date, open, high, low, close, volume, count, WAP, hasGaps): msg = { 'type': 'historicalData', 'reqId': reqId, 'date': date, 'open': open, 'high': high, 'low': low, 'close': close, 'volume': volume, 'count': count, 'WAP': WAP, 'hasGaps': hasGaps, 'ts': now() } self.msgs.put(msg)
async def read(self) -> None: if self._transport is None: raise ConnectionError() try: ircmsg: bytes = await self._reader.readuntil(b'\r\n') except ConnectionError: utils.logException() return try: ircmsg = ircmsg[:-2] if not ircmsg: return message: str = ircmsg.decode('utf-8') self._log_read(message) lib.ircmessage.parseMessage(self, message, utils.now()) except data.ConnectionReset: raise except data.LoginUnsuccessful: bot.globals.running = False raise
async def write(self, command: IrcMessage, *, channel: 'Optional[data.Channel]' = None, whisper: Optional[WhisperMessage] = None) -> None: if not isinstance(command, IrcMessage): raise TypeError() if self._transport is None: raise ConnectionError() try: messageBytes: bytes = str(command).encode('utf-8') timestamp: datetime = utils.now() self._writer.write(messageBytes) self._writer.write(b'\r\n') await self._writer.drain() self._on_write(command, timestamp, channel=channel) self._log_write(command, channel=channel, whisper=whisper, timestamp=timestamp) except Exception: utils.logException() raise
def currentTime(self, time): msg = {'type': 'currentTime', 'time': time, 'ts': now()} self.msgs.put(msg)
def managedAccounts(self, accountsList): msg = {'type': 'managedAccounts', 'accountsList': accountsList, 'ts': now()} self.msgs.put(msg)
def tickPrice(self, tickerId, field, px, _canAutoExecute): msg = {'type': 'tickPrice', 'symbol': self.inst_map[tickerId]['symbol'], 'field': field, 'price': px, 'ts': now()} self.msgs.put(msg)
def tickString(self, tickerId, tickType, value): msg = {'type': 'tickString', 'symbol': self.inst_map[tickerId]['symbol'], 'tickType': tickType, 'value': value, 'ts': now()} self.msgs.put(msg)
def position(self, account, contract, pos, avgCost): msg = {'type': 'position', 'account': account, 'contract': contract, 'pos': pos, 'avgCost': avgCost, 'ts': now()} self.msgs.put(msg)
def openOrder(self, orderId, contract, order, state): msg = {'type': 'openOrder', 'orderId': orderId, 'contract': contract, 'order': order, 'state': state, 'ts': now()} self.msgs.put(msg)
def openOrderEnd(self): msg = {'type': 'openOrderEnd', 'ts': now()} self.msgs.put(msg)
def nextValidId(self, orderId): msg = {'type': 'nextValidId', 'orderId': orderId, 'ts': now()} self.msgs.put(msg)
def positionEnd(self): msg = {'type': 'positionEnd', 'ts': now(), 'ts': now()} self.msgs.put(msg)
def test_now(self): self.assertIsInstance(utils.now(), datetime)
def tickSize(self, tickerId, field, sz): msg = {'type': 'tickSize', 'symbol': self.inst_map[tickerId]['symbol'], 'field': field, 'size': sz, 'ts': now()} self.msgs.put(msg)
def realtimeBar(self, reqId, time, open, high, low, close, volume, wap, count): msg = {'type': 'realtimeBar', 'reqId': reqId, 'time': time, 'open': open, 'high': high, 'low': low, 'close': close, 'volume': volume, 'count': count, 'WAP': WAP, 'ts': now()} self.msgs.put(msg)
def nextValidId(self, orderId): msg = {'type': 'nextValidId', 'orderId': orderId, 'ts': now()} self.msgs.put(msg)
def positionEnd(self): msg = {'type': 'positionEnd', 'ts': now(), 'ts': now()} self.msgs.put(msg)
def execDetailsEnd(self, reqId): msg = {'type': 'execDetailsEnd', 'reqId': reqId, 'ts': now()} self.msgs.put(msg)
def execDetailsEnd(self, reqId): msg = {'type': 'execDetailsEnd', 'reqId': reqId, 'ts': now()} self.msgs.put(msg)
def currentTime(self, time): msg = {'type': 'currentTime', 'time': time, 'ts': now()} self.msgs.put(msg)
def openOrderEnd(self): msg = {'type': 'openOrderEnd', 'ts': now()} self.msgs.put(msg)
async def commandMultiTwitch(args: ChatCommandArgs) -> bool: ''' Example Commands: !multitwitch << gives a link of linked multitwitch, available to everyone !multitwitch kadgar << available to everyone !multitwitch preference kadgar << available to everyone !multitwitch add !multitwitch add kappa !multitwitch drop !multitwitch reset !multitwitch remove kappa !multitwitch remove !multitwitch event kappa << owner command, does not perform auto removal The command should automatically remove inactive streams of more than 5 minutes or 15 minutes after the initial add if stream hasnt started ''' # TODO: mypy fix after https://github.com/python/mypy/issues/1855 currentTime: datetime = utils.now() db: DatabaseMain cursor: aioodbc.cursor query: str params: Tuple[Any, ...] paramsM: List[Tuple[Any, ...]] row: Tuple[Any, ...] group: str groupO: Optional[str] groups: List[Tuple[Any, ...]] event: Optional[bool] async with DatabaseMain.acquire() as db, await db.cursor() as cursor: if (len(args.message) < 2 or not args.permissions.moderator or args.message.lower[1] in library.multiUrls): cooldown: timedelta = timedelta(seconds=30) if args.permissions.moderator: cooldown = timedelta(seconds=10) if (not args.permissions.broadcaster and 'multitwitch' in args.chat.sessionData): since: timedelta since = currentTime - args.chat.sessionData['multitwitch'] if since < cooldown: return False query = 'SELECT twitchgroup FROM multitwitch WHERE broadcaster=?' await cursor.execute(query, (args.chat.channel, )) groupO = (await cursor.fetchone() or [None])[0] twitches: List[str] = [] if groupO: query = ''' SELECT broadcaster, addedTime, lastLive FROM multitwitch WHERE twitchgroup=? ORDER BY isEvent DESC, addedTime ASC ''' async for row in await cursor.execute(query, (groupO, )): broadcaster: str added: datetime live: datetime broadcaster, added, live = row if live is None: if currentTime - added > library.addedCooldown: continue else: if currentTime - live > library.liveCooldown: continue twitches.append(broadcaster) if not twitches: args.chat.send(f'https://www.twitch.tv/{args.chat.channel}') if args.permissions.moderator: args.chat.send('''\ Just do !multitwitch add <twitch user> to create/start a multitwitch link''') args.chat.sessionData['multitwitch'] = currentTime return True default: str = await args.data.getChatProperty( args.chat.channel, 'multitwitch', library.default, str) preference: str = await args.data.getChatProperty( args.nick, 'multitwitch', default, str) if (len(args.message) >= 2 and args.message.lower[1] in library.multiUrls): preference = args.message.lower[1] if len(twitches) == 1: args.chat.send('https://www.twitch.tv/' + twitches[0]) elif preference in library.multiUrls: args.chat.send(library.multiUrls[preference](twitches)) else: args.chat.send(library.multiUrls[library.default](twitches)) args.chat.sessionData['multitwitch'] = currentTime return True if args.message.lower[1] == 'preference': if len(args.message) < 2: await args.data.setChatProperty(args.nick, 'multitwitch', None) elif args.message.lower[2] in library.multiUrls: await args.data.setChatProperty(args.nick, 'multitwitch', args.message.lower[2]) else: args.chat.send('Unrecognized multitwitch site') if not args.permissions.moderator: return False if args.message.lower[1] == 'add': other: str if len(args.message) < 3: other = args.nick else: other = args.message.lower[2] if not await args.data.twitch_is_valid_user(other): args.chat.send(f'{other} is not a valid Twitch user') return True if other == args.chat.channel: args.chat.send('You cannot add yourself for multitwitch link') return True query = ''' SELECT broadcaster, twitchgroup, isEvent FROM multitwitch WHERE broadcaster IN (?, ?)''' params = args.chat.channel, other groups = [row async for row in await cursor.execute(query, params)] if len(groups) == 0: alphabet = ('0123456789' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz') group = ''.join(random.choice(alphabet) for i in range(7)) query = ''' INSERT INTO multitwitch (broadcaster, twitchgroup, addedTime) VALUES (?, ?, ?)''' paramsM = [(args.chat.channel, group, currentTime), (other, group, currentTime)] await cursor.executemany(query, paramsM) args.chat.send(f'''\ Created a multitwitch for {args.chat.channel} and {other}''') elif len(groups) == 1: group = groups[0][1] toAdd: str done: str toAdd = args.chat.channel if groups[0][0] == other else other done = args.chat.channel if groups[0][0] != other else other query = ''' INSERT INTO multitwitch (broadcaster, twitchgroup, addedTime) VALUES (?, ?, ?)''' await cursor.execute(query, (toAdd, group, currentTime)) args.chat.send(f'''\ Added {toAdd} to the multitwitch of {done} and others''') else: group = groups[0][1] g: Tuple[str, str, bool] for g in cast(List[Tuple[str, str, bool]], groups): if g[2]: group = g[1] break query = ''' UPDATE multitwitch SET twitchgroup=? WHERE twitchgroup=?''' paramsM = [(g[1], group) for g in groups if g[1] != group] if not paramsM: args.chat.send(f'''\ {args.chat.channel} and {other} are already in the same multitwitch''') return True await cursor.executemany(query, paramsM) args.chat.send(f'''\ Merged the multitwitches of {args.chat.channel} and {other}''') await db.commit() if args.message.lower[1] in ['drop', 'delete', 'del' 'remove', 'rem']: who: str if len(args.message) < 3: who = args.chat.channel else: who = args.message.lower[2] query = ''' SELECT twitchgroup, isEvent FROM multitwitch WHERE twitchgroup=(SELECT twitchgroup FROM multitwitch WHERE broadcaster=?) AND broadcaster=?''' await cursor.execute(query, (args.chat.channel, who)) groupO, event = await cursor.fetchone() or (None, None) if groupO is None: args.chat.send(f'''\ Multitwitch of {who} does not exist or is not part of the same multitwitch of \ {args.chat.channel}''') return True query = 'SELECT COUNT(*) FROM multitwitch WHERE twitchgroup=?' await cursor.execute(query, (groupO, )) if (await cursor.fetchone())[0] <= 2: query = 'DELETE FROM multitwitch WHERE twitchgroup=?' await cursor.execute(query, (groupO, )) if args.chat.channel == who: args.chat.send(f'''\ Reset the multitwitch of {args.chat.channel} and others''') else: args.chat.send(f'''\ Reset the multitwitch of {args.chat.channel} and {who}''') await db.commit() return True if not event: query = 'DELETE FROM multitwitch WHERE broadcaster=?' await cursor.execute(query, (who, )) if who == args.chat.channel: args.chat.send(f'''\ Removed {args.chat.channel} from a multitwitch''') else: args.chat.send(f'''\ Removed {who} from a multitwitch with {args.chat.channel}''') await db.commit() return True query = ''' SELECT COUNT(*) FROM multitwitch WHERE twitchgroup=? AND isEvent=FALSE UNION ALL SELECT COUNT(*) FROM multitwitch WHERE twitchgroup=? AND isEvent=TRUE''' await cursor.execute(query, (groupO, ) * 2) notEvent, = await cursor.fetchone() inEvent, = await cursor.fetchone() if notEvent > 0: args.chat.send(f'''\ Cannot remove {who} until all non-event users are removed''') return True query = 'DELETE FROM multitwitch WHERE broadcaster=?' await cursor.execute(query, (who, )) if who == args.chat.channel: args.chat.send(f'''\ Removed {args.chat.channel} from a multitwitch''') else: args.chat.send(f'''\ Removed {who} from a multitwitch with {args.chat.channel}''') await db.commit() if args.message.lower[1] == 'reset': query = 'SELECT twitchgroup FROM multitwitch WHERE broadcaster=?' await cursor.execute(query, (args.chat.channel, )) groupO, = await cursor.fetchone() or (None, ) if groupO is None: args.chat.send(f'''\ Multitwitch of {args.chat.channel} does not exist''') return True query = ''' SELECT COUNT(*) FROM multitwitch WHERE twitchgroup=? AND isEvent=FALSE UNION ALL SELECT COUNT(*) FROM multitwitch WHERE twitchgroup=? AND isEvent=TRUE''' await cursor.execute(query, (groupO, ) * 2) notEvent, = await cursor.fetchone() inEvent, = await cursor.fetchone() if notEvent > 0 and inEvent > 1: query = ''' DELETE FROM multitwitch WHERE twitchgroup=? AND isEvent=FALSE''' await cursor.execute(query, (groupO, )) args.chat.send('Reset the multitwitch of non-event users') await db.commit() return True query = 'DELETE FROM multitwitch WHERE twitchgroup=?' await cursor.execute(query, (groupO, )) args.chat.send(f'''\ Reset the multitwitch of {args.chat.channel} and others''') await db.commit() if args.message.lower[1] == 'event' and args.permissions.owner: if len(args.message) < 3: who = args.chat.channel else: who = args.message.lower[2] query = ''' SELECT twitchgroup, isEvent FROM multitwitch WHERE twitchgroup=(SELECT twitchgroup FROM multitwitch WHERE broadcaster=?) AND broadcaster=?''' await cursor.execute(query, (args.chat.channel, who)) groupO, event = await cursor.fetchone() or (None, None) if groupO is not None: query = 'UPDATE multitwitch SET isEvent=? WHERE broadcaster=?' await cursor.execute(query, ( not event, who, )) if not event: args.chat.send(f'{who} is marked as an event multitwitch') else: args.chat.send( f'{who} is unmarked from an event multitwitch') await db.commit() else: args.chat.send(f'''\ Multitwitch of {who} does not exist or is not part of the same multitwitch of \ {args.chat.channel}''') return True
def execDetails(self, reqId, contract, execution): msg = {'type': 'execDetails', 'reqId': reqId, 'contract': contract, 'execution': execution, 'ts': now()} self.msgs.put(msg)
def error_0(self, strvalue=None): msg = {'type': 'error_0', 'strvalue': strvalue, 'ts': now()} self.msgs.put(msg)
def error(self, id=None, errorCode=None, errorMsg=None): msg = {'type': 'error', 'id': id, 'errorCode': errorCode, 'errorMsg': errorMsg, 'ts': now()} self.msgs.put(msg)
def error_0(self, strvalue=None): msg = {'type': 'error_0', 'strvalue': strvalue, 'ts': now()} self.msgs.put(msg)