2017-08-02 23:41:02 +02:00
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import asyncio
import os
import sys
import logging
import math
from . Utils import Utils , DataHolder
import traceback
class BaseCommand ( ) :
Order = 0
def __init__ ( self , torchlight ) :
self . Logger = logging . getLogger ( __class__ . __name__ )
self . Torchlight = torchlight
self . Triggers = [ ]
self . Level = 0
2019-12-30 17:43:48 +01:00
def check_chat_cooldown ( self , player ) :
if player . ChatCooldown > self . Torchlight ( ) . Master . Loop . time ( ) :
cooldown = player . ChatCooldown - self . Torchlight ( ) . Master . Loop . time ( )
self . Torchlight ( ) . SayPrivate ( player , " You ' re on cooldown for the next {0:.1f} seconds. " . format ( cooldown ) )
return True
def check_disabled ( self , player ) :
Level = 0
if player . Access :
Level = player . Access [ " level " ]
Disabled = self . Torchlight ( ) . Disabled
if Disabled and ( Disabled > Level or Disabled == Level and Level < self . Torchlight ( ) . Config [ " AntiSpam " ] [ " ImmunityLevel " ] ) :
self . Torchlight ( ) . SayPrivate ( player , " Torchlight is currently disabled! " )
return True
2017-08-02 23:41:02 +02:00
async def _func ( self , message , player ) :
self . Logger . debug ( sys . _getframe ( ) . f_code . co_name )
2019-12-30 17:43:48 +01:00
2017-08-02 23:41:02 +02:00
class URLFilter ( BaseCommand ) :
Order = 1
import re
import aiohttp
import magic
import datetime
import json
import io
from bs4 import BeautifulSoup
from PIL import Image
def __init__ ( self , torchlight ) :
super ( ) . __init__ ( torchlight )
self . Triggers = [ self . re . compile ( r ''' (?i) \ b((?:https?://|www \ d { 0,3}[.]|[a-z0-9. \ -]+[.][a-z] { 2,4}/)(?:[^ \ s()<>]+| \ (([^ \ s()<>]+|( \ ([^ \ s()<>]+ \ )))* \ ))+(?: \ (([^ \ s()<>]+|( \ ([^ \ s()<>]+ \ )))* \ )|[^ \ s`!() \ [ \ ] {} ;: ' " .,<>?«»“”‘’])) ''' , self . re . IGNORECASE ) ]
self . Level = - 1
self . re_youtube = self . re . compile ( r ' .*?(?:youtube \ .com \ / \ S*(?:(?: \ /e(?:mbed))? \ /|watch \ ?(?: \ S*?&?v \ =))|youtu \ .be \ /)([a-zA-Z0-9_-] { 6,11}).*? ' )
async def URLInfo ( self , url , yt = False ) :
2018-09-21 15:03:49 +02:00
Text = None
2017-08-02 23:41:02 +02:00
Info = None
match = self . re_youtube . search ( url )
if match or yt :
Temp = DataHolder ( )
Time = None
if Temp ( url . find ( " &t= " ) ) != - 1 or Temp ( url . find ( " ?t= " ) ) != - 1 or Temp ( url . find ( " #t= " ) ) != - 1 :
TimeStr = url [ Temp . value + 3 : ] . split ( ' & ' ) [ 0 ] . split ( ' ? ' ) [ 0 ] . split ( ' # ' ) [ 0 ]
if TimeStr :
Time = Utils . ParseTime ( TimeStr )
2019-12-30 17:43:48 +01:00
Proc = await asyncio . create_subprocess_exec ( " youtube-dl " , " --dump-json " , " -g " , url ,
2017-08-02 23:41:02 +02:00
stdout = asyncio . subprocess . PIPE )
Out , _ = await Proc . communicate ( )
2019-12-30 17:43:48 +01:00
parts = Out . split ( b ' \n ' )
parts . pop ( ) # trailing new line
Info = parts . pop ( )
url = parts . pop ( )
2017-08-02 23:41:02 +02:00
url = url . strip ( ) . decode ( " ascii " )
Info = self . json . loads ( Info )
if Info [ " extractor_key " ] == " Youtube " :
self . Torchlight ( ) . SayChat ( " \x07 E52D27[YouTube] \x01 {0} | {1} | {2} /5.00 | {3:,} " . format (
Info [ " title " ] , str ( self . datetime . timedelta ( seconds = Info [ " duration " ] ) ) , round ( Info [ " average_rating " ] , 2 ) , int ( Info [ " view_count " ] ) ) )
else :
match = None
2018-09-21 15:03:49 +02:00
if Time :
url + = " #t= {0} " . format ( Time )
2017-08-02 23:41:02 +02:00
else :
try :
async with self . aiohttp . ClientSession ( ) as session :
Response = await asyncio . wait_for ( session . get ( url ) , 5 )
if Response :
ContentType = Response . headers . get ( " Content-Type " )
ContentLength = Response . headers . get ( " Content-Length " )
Content = await asyncio . wait_for ( Response . content . read ( 65536 ) , 5 )
if not ContentLength :
ContentLength = - 1
2018-09-21 15:03:49 +02:00
if ContentType . startswith ( " text " ) :
if ContentType . startswith ( " text/plain " ) :
Text = Content . decode ( " utf-8 " , errors = " ignore " )
else :
Soup = self . BeautifulSoup ( Content . decode ( " utf-8 " , errors = " ignore " ) , " lxml " )
if Soup . title :
self . Torchlight ( ) . SayChat ( " [URL] {0} " . format ( Soup . title . string ) )
2017-08-02 23:41:02 +02:00
elif ContentType . startswith ( " image " ) :
fp = self . io . BytesIO ( Content )
im = self . Image . open ( fp )
self . Torchlight ( ) . SayChat ( " [IMAGE] {0} | Width: {1} | Height: {2} | Size: {3} " . format ( im . format , im . size [ 0 ] , im . size [ 1 ] , Utils . HumanSize ( ContentLength ) ) )
fp . close ( )
else :
Filetype = self . magic . from_buffer ( bytes ( Content ) )
self . Torchlight ( ) . SayChat ( " [FILE] {0} | Size: {1} " . format ( Filetype , Utils . HumanSize ( ContentLength ) ) )
Response . close ( )
except Exception as e :
self . Torchlight ( ) . SayChat ( " Error: {0} " . format ( str ( e ) ) )
self . Logger . error ( traceback . format_exc ( ) )
self . Torchlight ( ) . LastUrl = url
2018-09-21 15:03:49 +02:00
return url , Text
2017-08-02 23:41:02 +02:00
async def _rfunc ( self , line , match , player ) :
Url = match . groups ( ) [ 0 ]
if not Url . startswith ( " http " ) and not Url . startswith ( " ftp " ) :
Url = " http:// " + Url
if line . startswith ( " !yt " ) :
2018-09-21 15:03:49 +02:00
URL , _ = await self . URLInfo ( Url , True )
2017-08-02 23:41:02 +02:00
return " !yt " + URL
2018-09-21 15:03:49 +02:00
if line . startswith ( " !dec " ) :
_ , text = await self . URLInfo ( Url , False )
if text :
return " !dec " + text
2017-08-02 23:41:02 +02:00
asyncio . ensure_future ( self . URLInfo ( Url ) )
return - 1
2019-12-30 17:43:48 +01:00
def FormatAccess ( Torchlight , player ) :
Answer = " # {0} \" {1} \" ( {2} ) is " . format ( player . UserID , player . Name , player . UniqueID )
Level = str ( 0 )
if player . Access :
Level = str ( player . Access [ " level " ] )
Answer + = " level {0!s} as {1} . " . format ( Level , player . Access [ " name " ] )
else :
Answer + = " not authenticated. "
2017-08-02 23:41:02 +02:00
2019-12-30 17:43:48 +01:00
if Level in Torchlight ( ) . Config [ " AudioLimits " ] :
Uses = Torchlight ( ) . Config [ " AudioLimits " ] [ Level ] [ " Uses " ]
TotalTime = Torchlight ( ) . Config [ " AudioLimits " ] [ Level ] [ " TotalTime " ]
2017-08-02 23:41:02 +02:00
2019-12-30 17:43:48 +01:00
if Uses > = 0 :
Answer + = " Uses: {0} / {1} " . format ( player . Storage [ " Audio " ] [ " Uses " ] , Uses )
if TotalTime > = 0 :
Answer + = " Time: {0} / {1} " . format ( round ( player . Storage [ " Audio " ] [ " TimeUsed " ] , 2 ) , round ( TotalTime , 2 ) )
2017-08-02 23:41:02 +02:00
2019-12-30 17:43:48 +01:00
return Answer
2017-08-02 23:41:02 +02:00
2019-12-30 17:43:48 +01:00
class Access ( BaseCommand ) :
def __init__ ( self , torchlight ) :
super ( ) . __init__ ( torchlight )
self . Triggers = [ " !access " ]
self . Level = 0
2017-08-02 23:41:02 +02:00
async def _func ( self , message , player ) :
self . Logger . debug ( sys . _getframe ( ) . f_code . co_name + ' ' + str ( message ) )
2019-12-30 17:43:48 +01:00
if self . check_chat_cooldown ( player ) :
return - 1
2017-08-02 23:41:02 +02:00
Count = 0
if message [ 0 ] == " !access " :
if message [ 1 ] :
return - 1
2019-12-30 17:43:48 +01:00
self . Torchlight ( ) . SayChat ( FormatAccess ( self . Torchlight , player ) , player )
return 0
class Who ( BaseCommand ) :
def __init__ ( self , torchlight ) :
super ( ) . __init__ ( torchlight )
self . Triggers = [ " !who " , " !whois " ]
self . Level = 1
2017-08-02 23:41:02 +02:00
2019-12-30 17:43:48 +01:00
async def _func ( self , message , player ) :
self . Logger . debug ( sys . _getframe ( ) . f_code . co_name + ' ' + str ( message ) )
Count = 0
if message [ 0 ] == " !who " :
2017-08-02 23:41:02 +02:00
for Player in self . Torchlight ( ) . Players :
if Player . Name . lower ( ) . find ( message [ 1 ] . lower ( ) ) != - 1 :
2019-12-30 17:43:48 +01:00
self . Torchlight ( ) . SayChat ( FormatAccess ( self . Torchlight , Player ) )
2017-08-02 23:41:02 +02:00
Count + = 1
if Count > = 3 :
break
elif message [ 0 ] == " !whois " :
for UniqueID , Access in self . Torchlight ( ) . Access :
if Access [ " name " ] . lower ( ) . find ( message [ 1 ] . lower ( ) ) != - 1 :
Player = self . Torchlight ( ) . Players . FindUniqueID ( UniqueID )
if Player :
2019-12-30 17:43:48 +01:00
self . Torchlight ( ) . SayChat ( FormatAccess ( self . Torchlight , Player ) )
2017-08-02 23:41:02 +02:00
else :
self . Torchlight ( ) . SayChat ( " #? \" {0} \" ( {1} ) is level {2!s} is currently offline. " . format ( Access [ " name " ] , UniqueID , Access [ " level " ] ) )
Count + = 1
if Count > = 3 :
break
return 0
class WolframAlpha ( BaseCommand ) :
import urllib . parse
import aiohttp
import xml . etree . ElementTree as etree
import re
def __init__ ( self , torchlight ) :
super ( ) . __init__ ( torchlight )
self . Triggers = [ " !cc " ]
2019-12-30 17:43:48 +01:00
self . Level = 3
2017-08-02 23:41:02 +02:00
def Clean ( self , Text ) :
return self . re . sub ( " [ ] { 2,} " , " " , Text . replace ( ' | ' , ' : ' ) . replace ( ' \n ' , ' | ' ) . replace ( ' ~~ ' , ' ≈ ' ) ) . strip ( )
2019-12-30 17:43:48 +01:00
async def Calculate ( self , Params , player ) :
2017-08-02 23:41:02 +02:00
async with self . aiohttp . ClientSession ( ) as session :
Response = await asyncio . wait_for ( session . get ( " http://api.wolframalpha.com/v2/query " , params = Params ) , 10 )
if not Response :
return 1
Data = await asyncio . wait_for ( Response . text ( ) , 5 )
if not Data :
return 2
Root = self . etree . fromstring ( Data )
# Find all pods with plaintext answers
# Filter out None -answers, strip strings and filter out the empty ones
Pods = list ( filter ( None , [ p . text . strip ( ) for p in Root . findall ( ' .//subpod/plaintext ' ) if p is not None and p . text is not None ] ) )
# no answer pods found, check if there are didyoumeans-elements
if not Pods :
Didyoumeans = Root . find ( " didyoumeans " )
# no support for future stuff yet, TODO?
if not Didyoumeans :
# If there's no pods, the question clearly wasn't understood
2019-12-30 17:43:48 +01:00
self . Torchlight ( ) . SayChat ( " Sorry, couldn ' t understand the question. " , player )
2017-08-02 23:41:02 +02:00
return 3
Options = [ ]
for Didyoumean in Didyoumeans :
Options . append ( " \" {0} \" " . format ( Didyoumean . text ) )
Line = " or " . join ( Options )
Line = " Did you mean {0} ? " . format ( Line )
2019-12-30 17:43:48 +01:00
self . Torchlight ( ) . SayChat ( Line , player )
2017-08-02 23:41:02 +02:00
return 0
# If there's only one pod with text, it's probably the answer
# example: "integral x²"
if len ( Pods ) == 1 :
Answer = self . Clean ( Pods [ 0 ] )
2019-12-30 17:43:48 +01:00
self . Torchlight ( ) . SayChat ( Answer , player )
2017-08-02 23:41:02 +02:00
return 0
# If there's multiple pods, first is the question interpretation
Question = self . Clean ( Pods [ 0 ] . replace ( ' | ' , ' ' ) . replace ( ' \n ' , ' ' ) )
# and second is the best answer
Answer = self . Clean ( Pods [ 1 ] )
2019-12-30 17:43:48 +01:00
self . Torchlight ( ) . SayChat ( " {0} = {1} " . format ( Question , Answer ) , player )
2017-08-02 23:41:02 +02:00
return 0
async def _func ( self , message , player ) :
self . Logger . debug ( sys . _getframe ( ) . f_code . co_name + ' ' + str ( message ) )
2018-08-13 08:19:29 +02:00
2019-12-30 17:43:48 +01:00
if self . check_chat_cooldown ( player ) :
return - 1
2018-08-13 08:19:29 +02:00
2019-12-30 17:43:48 +01:00
if self . check_disabled ( player ) :
return - 1
2018-08-13 08:19:29 +02:00
2017-08-02 23:41:02 +02:00
Params = dict ( { " input " : message [ 1 ] , " appid " : self . Torchlight ( ) . Config [ " WolframAPIKey " ] } )
2019-12-30 17:43:48 +01:00
Ret = await self . Calculate ( Params , player )
2017-08-02 23:41:02 +02:00
return Ret
2019-12-30 17:43:48 +01:00
class UrbanDictionary ( BaseCommand ) :
import aiohttp
def __init__ ( self , torchlight ) :
super ( ) . __init__ ( torchlight )
self . Triggers = [ " !define " , " !ud " ]
self . Level = 0
async def _func ( self , message , player ) :
self . Logger . debug ( sys . _getframe ( ) . f_code . co_name + ' ' + str ( message ) )
if self . check_chat_cooldown ( player ) :
return - 1
if self . check_disabled ( player ) :
return - 1
async with self . aiohttp . ClientSession ( ) as session :
Response = await asyncio . wait_for ( session . get ( " https://api.urbandictionary.com/v0/define?term= {0} " . format ( message [ 1 ] ) ) , 5 )
if not Response :
return 1
Data = await asyncio . wait_for ( Response . json ( ) , 5 )
if not Data :
return 3
if not ' list ' in Data or not Data [ " list " ] :
self . Torchlight ( ) . SayChat ( " [UB] No definition found for: {} " . format ( message [ 1 ] ) , player )
return 4
def print_item ( item ) :
self . Torchlight ( ) . SayChat ( " [UD] {word} ( {thumbs_up} / {thumbs_down} ): {definition} \n {example} " . format ( * * item ) , player )
print_item ( Data [ " list " ] [ 0 ] )
class OpenWeather ( BaseCommand ) :
import aiohttp
import geoip2 . database
def __init__ ( self , torchlight ) :
super ( ) . __init__ ( torchlight )
self . GeoIP = self . geoip2 . database . Reader ( " /usr/share/GeoIP/GeoLite2-City.mmdb " )
self . Triggers = [ " !w " , " !vv " ]
self . Level = 0
async def _func ( self , message , player ) :
self . Logger . debug ( sys . _getframe ( ) . f_code . co_name + ' ' + str ( message ) )
if self . check_chat_cooldown ( player ) :
return - 1
if self . check_disabled ( player ) :
return - 1
if not message [ 1 ] :
# Use GeoIP location
info = self . GeoIP . city ( player . Address . split ( " : " ) [ 0 ] )
Search = " lat= {} &lon= {} " . format ( info . location . latitude , info . location . longitude )
else :
Search = " q= {} " . format ( message [ 1 ] )
async with self . aiohttp . ClientSession ( ) as session :
Response = await asyncio . wait_for ( session . get ( " https://api.openweathermap.org/data/2.5/weather?APPID= {0} &units=metric& {1} " . format (
self . Torchlight ( ) . Config [ " OpenWeatherAPIKey " ] , Search ) ) , 5 )
if not Response :
return 2
Data = await asyncio . wait_for ( Response . json ( ) , 5 )
if not Data :
return 3
if Data [ " cod " ] != 200 :
self . Torchlight ( ) . SayPrivate ( player , " [OW] {0} " . format ( Data [ " message " ] ) )
return 5
degToCardinal = lambda d : [ " N " , " NE " , " E " , " SE " , " S " , " SW " , " W " , " NW " ] [ int ( ( ( d + 22.5 ) / 45.0 ) % 8 ) ]
if " deg " in Data [ " wind " ] :
windDir = degToCardinal ( Data [ " wind " ] [ " deg " ] )
else :
windDir = " ? "
timezone = " {} {} " . format ( ' + ' if Data [ " timezone " ] > 0 else ' ' , int ( Data [ " timezone " ] / 3600 ) )
if Data [ " timezone " ] % 3600 != 0 :
timezone + = " : {} " . format ( ( Data [ " timezone " ] % 3600 ) / 60 )
self . Torchlight ( ) . SayChat ( " [ {} , {} ](UTC {} ) {} °C ( {} / {} ) {} : {} | Wind {} {} kph | Clouds: {} %% | Humidity: {} %% " . format ( Data [ " name " ] , Data [ " sys " ] [ " country " ] , timezone ,
Data [ " main " ] [ " temp " ] , Data [ " main " ] [ " temp_min " ] , Data [ " main " ] [ " temp_max " ] , Data [ " weather " ] [ 0 ] [ " main " ] , Data [ " weather " ] [ 0 ] [ " description " ] ,
windDir , Data [ " wind " ] [ " speed " ] , Data [ " clouds " ] [ " all " ] , Data [ " main " ] [ " humidity " ] ) , player )
return 0
'''
2017-08-02 23:41:02 +02:00
class WUnderground ( BaseCommand ) :
import aiohttp
def __init__ ( self , torchlight ) :
super ( ) . __init__ ( torchlight )
self . Triggers = [ " !w " ]
self . Level = 0
async def _func ( self , message , player ) :
if not message [ 1 ] :
# Use IP address
Search = " autoip "
Additional = " ?geo_ip= {0} " . format ( player . Address . split ( " : " ) [ 0 ] )
else :
async with self . aiohttp . ClientSession ( ) as session :
Response = await asyncio . wait_for ( session . get ( " http://autocomplete.wunderground.com/aq?format=JSON&query= {0} " . format ( message [ 1 ] ) ) , 5 )
if not Response :
return 2
Data = await asyncio . wait_for ( Response . json ( ) , 5 )
if not Data :
return 3
if not Data [ " RESULTS " ] :
self . Torchlight ( ) . SayPrivate ( player , " [WU] No cities match your search query. " )
return 4
Search = Data [ " RESULTS " ] [ 0 ] [ " name " ]
Additional = " "
async with self . aiohttp . ClientSession ( ) as session :
Response = await asyncio . wait_for ( session . get ( " http://api.wunderground.com/api/ {0} /conditions/q/ {1} .json {2} " . format (
self . Torchlight ( ) . Config [ " WundergroundAPIKey " ] , Search , Additional ) ) , 5 )
if not Response :
return 2
Data = await asyncio . wait_for ( Response . json ( ) , 5 )
if not Data :
return 3
if " error " in Data [ " response " ] :
self . Torchlight ( ) . SayPrivate ( player , " [WU] {0} . " . format ( Data [ " response " ] [ " error " ] [ " description " ] ) )
return 5
if not " current_observation " in Data :
Choices = str ( )
NumResults = len ( Data [ " response " ] [ " results " ] )
for i , Result in enumerate ( Data [ " response " ] [ " results " ] ) :
Choices + = " {0} , {1} " . format ( Result [ " city " ] ,
Result [ " state " ] if Result [ " state " ] else Result [ " country_iso3166 " ] )
if i < NumResults - 1 :
Choices + = " | "
self . Torchlight ( ) . SayPrivate ( player , " [WU] Did you mean: {0} " . format ( Choices ) )
return 6
Observation = Data [ " current_observation " ]
self . Torchlight ( ) . SayChat ( " [ {0} , {1} ] {2} °C ( {3} F) {4} | Wind {5} {6} kph ( {7} mph) | Humidity: {8} " . format ( Observation [ " display_location " ] [ " city " ] ,
Observation [ " display_location " ] [ " state " ] if Observation [ " display_location " ] [ " state " ] else Observation [ " display_location " ] [ " country_iso3166 " ] ,
Observation [ " temp_c " ] , Observation [ " temp_f " ] , Observation [ " weather " ] ,
Observation [ " wind_dir " ] , Observation [ " wind_kph " ] , Observation [ " wind_mph " ] ,
Observation [ " relative_humidity " ] ) )
return 0
2019-12-30 17:43:48 +01:00
'''
2017-08-02 23:41:02 +02:00
2018-08-13 08:19:29 +02:00
class VoteDisable ( BaseCommand ) :
def __init__ ( self , torchlight ) :
super ( ) . __init__ ( torchlight )
self . Triggers = [ " !votedisable " , " !disablevote " ]
self . Level = 0
async def _func ( self , message , player ) :
self . Logger . debug ( sys . _getframe ( ) . f_code . co_name + ' ' + str ( message ) )
2019-12-30 17:43:48 +01:00
2018-08-13 08:19:29 +02:00
if self . Torchlight ( ) . Disabled :
self . Torchlight ( ) . SayPrivate ( player , " Torchlight is already disabled for the duration of this map. " )
return
self . Torchlight ( ) . DisableVotes . add ( player . UniqueID )
have = len ( self . Torchlight ( ) . DisableVotes )
needed = len ( self . Torchlight ( ) . Players ) / / 5
if have > = needed :
self . Torchlight ( ) . SayChat ( " Torchlight has been disabled for the duration of this map. " )
self . Torchlight ( ) . Disabled = 6
else :
self . Torchlight ( ) . SayPrivate ( player , " Torchlight needs {0} more disable votes to be disabled. " . format ( needed - have ) )
2017-08-02 23:41:02 +02:00
class VoiceCommands ( BaseCommand ) :
import json
import random
def __init__ ( self , torchlight ) :
super ( ) . __init__ ( torchlight )
2019-12-30 17:43:48 +01:00
self . Triggers = [ " !random " , " !search " ]
2017-08-02 23:41:02 +02:00
self . Level = 0
def LoadTriggers ( self ) :
try :
with open ( " triggers.json " , " r " ) as fp :
2019-12-30 17:43:48 +01:00
Triggers = self . json . load ( fp )
2017-08-02 23:41:02 +02:00
except ValueError as e :
self . Logger . error ( sys . _getframe ( ) . f_code . co_name + ' ' + str ( e ) )
self . Torchlight ( ) . SayChat ( str ( e ) )
2019-12-30 17:43:48 +01:00
self . VoiceTriggers = dict ( )
for Line in Triggers :
for Trigger in Line [ " names " ] :
self . VoiceTriggers [ Trigger ] = Line [ " sound " ]
2017-08-02 23:41:02 +02:00
def _setup ( self ) :
self . Logger . debug ( sys . _getframe ( ) . f_code . co_name )
self . LoadTriggers ( )
2019-12-30 17:43:48 +01:00
for Trigger in self . VoiceTriggers . keys ( ) :
self . Triggers . append ( Trigger )
2017-08-02 23:41:02 +02:00
async def _func ( self , message , player ) :
self . Logger . debug ( sys . _getframe ( ) . f_code . co_name + ' ' + str ( message ) )
2019-12-30 17:43:48 +01:00
if self . check_disabled ( player ) :
return - 1
2017-08-02 23:41:02 +02:00
Level = 0
if player . Access :
Level = player . Access [ " level " ]
2019-12-30 17:43:48 +01:00
message [ 0 ] = message [ 0 ] . lower ( )
message [ 1 ] = message [ 1 ] . lower ( )
if message [ 0 ] [ 0 ] != ' ! ' and Level < 2 :
2017-08-02 23:41:02 +02:00
return 1
2019-12-30 17:43:48 +01:00
if message [ 0 ] == " !search " :
res = [ ]
for key in self . VoiceTriggers . keys ( ) :
if message [ 1 ] in key . lower ( ) :
res . append ( key )
self . Torchlight ( ) . SayPrivate ( player , " {} results: {} " . format ( len ( res ) , " , " . join ( res ) ) )
return 0
elif Level < 2 :
return 0
2017-08-02 23:41:02 +02:00
2019-12-30 17:43:48 +01:00
if message [ 0 ] == " !random " :
Trigger = self . random . choice ( list ( self . VoiceTriggers . values ( ) ) )
if isinstance ( Trigger , list ) :
Sound = self . random . choice ( Trigger )
2017-08-02 23:41:02 +02:00
else :
2019-12-30 17:43:48 +01:00
Sound = Trigger
2017-08-02 23:41:02 +02:00
else :
2019-12-30 17:43:48 +01:00
Sounds = self . VoiceTriggers [ message [ 0 ] ]
2017-08-02 23:41:02 +02:00
2019-12-30 17:43:48 +01:00
try :
Num = int ( message [ 1 ] )
except ValueError :
Num = None
if isinstance ( Sounds , list ) :
if Num and Num > 0 and Num < = len ( Sounds ) :
Sound = Sounds [ Num - 1 ]
elif message [ 1 ] :
searching = message [ 1 ] . startswith ( ' ? ' )
search = message [ 1 ] [ 1 : ] if searching else message [ 1 ]
Sound = None
names = [ ]
matches = [ ]
for sound in Sounds :
name = os . path . splitext ( os . path . basename ( sound ) ) [ 0 ]
names . append ( name )
if search and search in name . lower ( ) :
matches . append ( ( name , sound ) )
if matches :
matches . sort ( key = lambda t : len ( t [ 0 ] ) )
mlist = [ t [ 0 ] for t in matches ]
if searching :
self . Torchlight ( ) . SayPrivate ( player , " {} results: {} " . format ( len ( mlist ) , " , " . join ( mlist ) ) )
return 0
Sound = matches [ 0 ] [ 1 ]
if len ( matches ) > 1 :
self . Torchlight ( ) . SayPrivate ( player , " Multiple matches: {} " . format ( " , " . join ( mlist ) ) )
if not Sound and not Num :
if not searching :
self . Torchlight ( ) . SayPrivate ( player , " Couldn ' t find {} in list of sounds. " . format ( message [ 1 ] ) )
self . Torchlight ( ) . SayPrivate ( player , " , " . join ( names ) )
return 1
elif Num :
self . Torchlight ( ) . SayPrivate ( player , " Number {} is out of bounds, max {} . " . format ( Num , len ( Sounds ) ) )
return 1
else :
Sound = self . random . choice ( Sounds )
else :
Sound = Sounds
if not Sound :
return 1
2017-08-02 23:41:02 +02:00
Path = os . path . abspath ( os . path . join ( " sounds " , Sound ) )
AudioClip = self . Torchlight ( ) . AudioManager . AudioClip ( player , " file:// " + Path )
if not AudioClip :
return 1
return AudioClip . Play ( )
2019-12-30 17:43:48 +01:00
2017-08-02 23:41:02 +02:00
class YouTube ( BaseCommand ) :
def __init__ ( self , torchlight ) :
super ( ) . __init__ ( torchlight )
self . Triggers = [ " !yt " ]
2019-12-30 17:43:48 +01:00
self . Level = 3
2017-08-02 23:41:02 +02:00
async def _func ( self , message , player ) :
self . Logger . debug ( sys . _getframe ( ) . f_code . co_name + ' ' + str ( message ) )
2019-12-30 17:43:48 +01:00
if self . check_disabled ( player ) :
return - 1
2017-08-02 23:41:02 +02:00
2019-12-30 17:43:48 +01:00
if self . Torchlight ( ) . LastUrl :
message [ 1 ] = message [ 1 ] . replace ( " !last " , self . Torchlight ( ) . LastUrl )
2017-08-02 23:41:02 +02:00
Temp = DataHolder ( )
Time = None
if Temp ( message [ 1 ] . find ( " &t= " ) ) != - 1 or Temp ( message [ 1 ] . find ( " ?t= " ) ) != - 1 or Temp ( message [ 1 ] . find ( " #t= " ) ) != - 1 :
TimeStr = message [ 1 ] [ Temp . value + 3 : ] . split ( ' & ' ) [ 0 ] . split ( ' ? ' ) [ 0 ] . split ( ' # ' ) [ 0 ]
if TimeStr :
Time = Utils . ParseTime ( TimeStr )
AudioClip = self . Torchlight ( ) . AudioManager . AudioClip ( player , message [ 1 ] )
if not AudioClip :
return 1
return AudioClip . Play ( Time )
class YouTubeSearch ( BaseCommand ) :
import json
import datetime
def __init__ ( self , torchlight ) :
super ( ) . __init__ ( torchlight )
self . Triggers = [ " !yts " ]
2019-12-30 17:43:48 +01:00
self . Level = 3
2017-08-02 23:41:02 +02:00
async def _func ( self , message , player ) :
self . Logger . debug ( sys . _getframe ( ) . f_code . co_name + ' ' + str ( message ) )
2019-12-30 17:43:48 +01:00
if self . check_disabled ( player ) :
return - 1
2017-08-02 23:41:02 +02:00
Temp = DataHolder ( )
Time = None
if Temp ( message [ 1 ] . find ( " &t= " ) ) != - 1 or Temp ( message [ 1 ] . find ( " ?t= " ) ) != - 1 or Temp ( message [ 1 ] . find ( " #t= " ) ) != - 1 :
TimeStr = message [ 1 ] [ Temp . value + 3 : ] . split ( ' & ' ) [ 0 ] . split ( ' ? ' ) [ 0 ] . split ( ' # ' ) [ 0 ]
if TimeStr :
Time = Utils . ParseTime ( TimeStr )
message [ 1 ] = message [ 1 ] [ : Temp . value ]
Proc = await asyncio . create_subprocess_exec ( " youtube-dl " , " --dump-json " , " -xg " , " ytsearch: " + message [ 1 ] ,
stdout = asyncio . subprocess . PIPE )
Out , _ = await Proc . communicate ( )
url , Info = Out . split ( b ' \n ' , maxsplit = 1 )
url = url . strip ( ) . decode ( " ascii " )
Info = self . json . loads ( Info )
if Info [ " extractor_key " ] == " Youtube " :
self . Torchlight ( ) . SayChat ( " \x07 E52D27[YouTube] \x01 {0} | {1} | {2} /5.00 | {3:,} " . format (
2019-12-30 17:43:48 +01:00
Info [ " title " ] , str ( self . datetime . timedelta ( seconds = Info [ " duration " ] ) ) , round ( Info [ " average_rating " ] or 0 , 2 ) , int ( Info [ " view_count " ] ) ) )
2017-08-02 23:41:02 +02:00
AudioClip = self . Torchlight ( ) . AudioManager . AudioClip ( player , url )
if not AudioClip :
return 1
self . Torchlight ( ) . LastUrl = url
return AudioClip . Play ( Time )
2019-12-30 17:43:48 +01:00
2017-08-02 23:41:02 +02:00
class Say ( BaseCommand ) :
import gtts
import tempfile
2018-08-13 08:19:29 +02:00
VALID_LANGUAGES = [ lang for lang in gtts . lang . tts_langs ( ) . keys ( ) ]
2017-08-02 23:41:02 +02:00
def __init__ ( self , torchlight ) :
super ( ) . __init__ ( torchlight )
self . Triggers = [ ( " !say " , 4 ) ]
2019-12-30 17:43:48 +01:00
self . Level = 2
2017-08-02 23:41:02 +02:00
async def Say ( self , player , language , message ) :
2018-08-13 08:19:29 +02:00
GTTS = self . gtts . gTTS ( text = message , lang = language )
2017-08-02 23:41:02 +02:00
TempFile = self . tempfile . NamedTemporaryFile ( delete = False )
GTTS . write_to_fp ( TempFile )
TempFile . close ( )
AudioClip = self . Torchlight ( ) . AudioManager . AudioClip ( player , " file:// " + TempFile . name )
if not AudioClip :
os . unlink ( TempFile . name )
return 1
if AudioClip . Play ( ) :
AudioClip . AudioPlayer . AddCallback ( " Stop " , lambda : os . unlink ( TempFile . name ) )
return 0
else :
os . unlink ( TempFile . name )
return 1
async def _func ( self , message , player ) :
self . Logger . debug ( sys . _getframe ( ) . f_code . co_name + ' ' + str ( message ) )
2019-12-30 17:43:48 +01:00
if self . check_disabled ( player ) :
return - 1
2017-08-02 23:41:02 +02:00
if not message [ 1 ] :
return 1
Language = " en "
if len ( message [ 0 ] ) > 4 :
Language = message [ 0 ] [ 4 : ]
if not Language in self . VALID_LANGUAGES :
return 1
asyncio . ensure_future ( self . Say ( player , Language , message [ 1 ] ) )
return 0
2019-12-30 17:43:48 +01:00
'''
2018-09-21 15:03:49 +02:00
class DECTalk ( BaseCommand ) :
import tempfile
def __init__ ( self , torchlight ) :
super ( ) . __init__ ( torchlight )
self . Triggers = [ " !dec " ]
self . Level = 0
async def Say ( self , player , message ) :
message = " [:phoneme on] " + message
TempFile = self . tempfile . NamedTemporaryFile ( delete = False )
TempFile . close ( )
Proc = await asyncio . create_subprocess_exec ( " wine " , " say.exe " , " -w " , TempFile . name ,
cwd = " dectalk " , stdin = asyncio . subprocess . PIPE )
await Proc . communicate ( message . encode ( ' utf-8 ' , errors = ' ignore ' ) )
AudioClip = self . Torchlight ( ) . AudioManager . AudioClip ( player , " file:// " + TempFile . name )
if not AudioClip :
os . unlink ( TempFile . name )
return 1
if AudioClip . Play ( None , " -af " , " volume=10dB " ) :
AudioClip . AudioPlayer . AddCallback ( " Stop " , lambda : os . unlink ( TempFile . name ) )
return 0
else :
os . unlink ( TempFile . name )
return 1
async def _func ( self , message , player ) :
self . Logger . debug ( sys . _getframe ( ) . f_code . co_name + ' ' + str ( message ) )
2019-12-30 17:43:48 +01:00
if self . check_disabled ( player ) :
return - 1
2018-09-21 15:03:49 +02:00
if not message [ 1 ] :
return 1
asyncio . ensure_future ( self . Say ( player , message [ 1 ] ) )
return 0
2019-12-30 17:43:48 +01:00
'''
2018-09-21 15:03:49 +02:00
2017-08-02 23:41:02 +02:00
class Stop ( BaseCommand ) :
def __init__ ( self , torchlight ) :
super ( ) . __init__ ( torchlight )
self . Triggers = [ " !stop " ]
self . Level = 0
async def _func ( self , message , player ) :
self . Logger . debug ( sys . _getframe ( ) . f_code . co_name + ' ' + str ( message ) )
self . Torchlight ( ) . AudioManager . Stop ( player , message [ 1 ] )
return True
class EnableDisable ( BaseCommand ) :
def __init__ ( self , torchlight ) :
super ( ) . __init__ ( torchlight )
self . Triggers = [ " !enable " , " !disable " ]
self . Level = 3
async def _func ( self , message , player ) :
self . Logger . debug ( sys . _getframe ( ) . f_code . co_name + ' ' + str ( message ) )
2019-12-30 17:43:48 +01:00
2017-08-02 23:41:02 +02:00
if message [ 0 ] == " !enable " :
if self . Torchlight ( ) . Disabled :
if self . Torchlight ( ) . Disabled > player . Access [ " level " ] :
2019-12-30 17:43:48 +01:00
self . Torchlight ( ) . SayPrivate ( player , " You don ' t have access to enable torchlight, since it was disabled by a higher level user. " )
2017-08-02 23:41:02 +02:00
return 1
self . Torchlight ( ) . SayChat ( " Torchlight has been enabled for the duration of this map - Type !disable to disable it again. " )
self . Torchlight ( ) . Disabled = False
elif message [ 0 ] == " !disable " :
2019-12-30 17:43:48 +01:00
if self . Torchlight ( ) . Disabled > player . Access [ " level " ] :
self . Torchlight ( ) . SayPrivate ( player , " You don ' t have access to disable torchlight, since it was already disabled by a higher level user. " )
return 1
self . Torchlight ( ) . SayChat ( " Torchlight has been disabled for the duration of this map - Type !enable to enable it again. " )
2017-08-02 23:41:02 +02:00
self . Torchlight ( ) . Disabled = player . Access [ " level " ]
class AdminAccess ( BaseCommand ) :
from collections import OrderedDict
def __init__ ( self , torchlight ) :
super ( ) . __init__ ( torchlight )
self . Triggers = [ " !access " ]
self . Level = 4
def ReloadValidUsers ( self ) :
self . Torchlight ( ) . Access . Load ( )
for Player in self . Torchlight ( ) . Players :
Access = self . Torchlight ( ) . Access [ Player . UniqueID ]
Player . Access = Access
async def _func ( self , message , player ) :
self . Logger . debug ( sys . _getframe ( ) . f_code . co_name + ' ' + str ( message ) )
if not message [ 1 ] :
return - 1
if message [ 1 ] . lower ( ) == " reload " :
self . ReloadValidUsers ( )
self . Torchlight ( ) . SayChat ( " Loaded access list with {0} users " . format ( len ( self . Torchlight ( ) . Access ) ) )
elif message [ 1 ] . lower ( ) == " save " :
self . Torchlight ( ) . Access . Save ( )
self . Torchlight ( ) . SayChat ( " Saved access list with {0} users " . format ( len ( self . Torchlight ( ) . Access ) ) )
# Modify access
else :
Player = None
Buf = message [ 1 ]
Temp = Buf . find ( " as " )
if Temp != - 1 :
try :
Regname , Level = Buf [ Temp + 4 : ] . rsplit ( ' ' , 1 )
except ValueError as e :
self . Torchlight ( ) . SayChat ( str ( e ) )
return 1
Regname = Regname . strip ( )
Level = Level . strip ( )
Buf = Buf [ : Temp ] . strip ( )
else :
try :
Buf , Level = Buf . rsplit ( ' ' , 1 )
except ValueError as e :
self . Torchlight ( ) . SayChat ( str ( e ) )
return 2
Buf = Buf . strip ( )
Level = Level . strip ( )
# Find user by User ID
if Buf [ 0 ] == ' # ' and Buf [ 1 : ] . isnumeric ( ) :
Player = self . Torchlight ( ) . Players . FindUserID ( int ( Buf [ 1 : ] ) )
# Search user by name
else :
for Player_ in self . Torchlight ( ) . Players :
if Player_ . Name . lower ( ) . find ( Buf . lower ( ) ) != - 1 :
Player = Player_
break
if not Player :
self . Torchlight ( ) . SayChat ( " Couldn ' t find user: {0} " . format ( Buf ) )
return 3
if Level . isnumeric ( ) or ( Level . startswith ( ' - ' ) and Level [ 1 : ] . isdigit ( ) ) :
Level = int ( Level )
if Level > = player . Access [ " level " ] and player . Access [ " level " ] < 10 :
self . Torchlight ( ) . SayChat ( " Trying to assign level {0} , which is higher or equal than your level ( {1} ) " . format ( Level , player . Access [ " level " ] ) )
return 4
if Player . Access :
if Player . Access [ " level " ] > = player . Access [ " level " ] and player . Access [ " level " ] < 10 :
self . Torchlight ( ) . SayChat ( " Trying to modify level {0} , which is higher or equal than your level ( {1} ) " . format ( Player . Access [ " level " ] , player . Access [ " level " ] ) )
return 5
if " Regname " in locals ( ) :
self . Torchlight ( ) . SayChat ( " Changed \" {0} \" ( {1} ) as {2} level/name from {3} to {4} as {5} " . format (
Player . Name , Player . UniqueID , Player . Access [ " name " ] , Player . Access [ " level " ] , Level , Regname ) )
Player . Access [ " name " ] = Regname
else :
self . Torchlight ( ) . SayChat ( " Changed \" {0} \" ( {1} ) as {2} level from {3} to {4} " . format (
Player . Name , Player . UniqueID , Player . Access [ " name " ] , Player . Access [ " level " ] , Level ) )
Player . Access [ " level " ] = Level
self . Torchlight ( ) . Access [ Player . UniqueID ] = Player . Access
else :
if not " Regname " in locals ( ) :
Regname = Player . Name
self . Torchlight ( ) . Access [ Player . UniqueID ] = self . OrderedDict ( [ ( " name " , Regname ) , ( " level " , Level ) ] )
Player . Access = self . Torchlight ( ) . Access [ Player . UniqueID ]
self . Torchlight ( ) . SayChat ( " Added \" {0} \" ( {1} ) to access list as {2} with level {3} " . format ( Player . Name , Player . UniqueID , Regname , Level ) )
else :
if Level == " revoke " and Player . Access :
if Player . Access [ " level " ] > = player . Access [ " level " ] and player . Access [ " level " ] < 10 :
self . Torchlight ( ) . SayChat ( " Trying to revoke level {0} , which is higher or equal than your level ( {1} ) " . format ( Player . Access [ " level " ] , player . Access [ " level " ] ) )
return 6
self . Torchlight ( ) . SayChat ( " Removed \" {0} \" ( {1} ) from access list (was {2} with level {3} ) " . format (
Player . Name , Player . UniqueID , Player . Access [ " name " ] , Player . Access [ " level " ] ) )
del self . Torchlight ( ) . Access [ Player . UniqueID ]
Player . Access = None
return 0
2019-12-30 17:43:48 +01:00
class Reload ( BaseCommand ) :
def __init__ ( self , torchlight ) :
super ( ) . __init__ ( torchlight )
self . Triggers = [ " !reload " ]
self . Level = 4
async def _func ( self , message , player ) :
self . Logger . debug ( sys . _getframe ( ) . f_code . co_name + ' ' + str ( message ) )
self . Torchlight ( ) . Reload ( )
return 0
2017-08-02 23:41:02 +02:00
class Exec ( BaseCommand ) :
def __init__ ( self , torchlight ) :
super ( ) . __init__ ( torchlight )
self . Triggers = [ " !exec " ]
2019-12-30 17:43:48 +01:00
self . Level = 100
2017-08-02 23:41:02 +02:00
async def _func ( self , message , player ) :
self . Logger . debug ( sys . _getframe ( ) . f_code . co_name + ' ' + str ( message ) )
try :
Response = eval ( message [ 1 ] )
except Exception as e :
self . Torchlight ( ) . SayChat ( " Error: {0} " . format ( str ( e ) ) )
return 1
self . Torchlight ( ) . SayChat ( str ( Response ) )
return 0