Code:
"""A bot to list all members of a server as well as make ticket channels"""
import csv
import sys
import time
import re
import *******
import datetime
import asyncio
import requests
import os
import threading
import platform
import sqlite3
import urllib.request
from *******.ext import commands
from *******.ex*****mmands import Bot
from electrum import bitcoin
from electrum import keystore
if platform.system() == "Windows":
from win32com.client import Dispatch
# Global Constants
token = '0'
command_prefix = '-'
bot_name = "Duke's Bot"
seeds = {}
progress_bar = None
bot = None
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
conn = None
c = None
# Create files and directories
if not os.path.isdir('database/'):
os.mkdir('database/')
if not os.path.isfile('database/data.db'):
conn = sqlite3.connect('database/data.db')
c = conn.cursor()
c.execute('''CREATE TABLE servers
(serverid TEXT, servername TEXT, prefix TEXT, ticketnumber INT, intro TEXT)''')
c.execute('''CREATE TABLE IF NOT EXISTS Addresses
(serverid TEXT, address TEXT, balance REAL, ticket INT)''')
conn.commit()
else:
conn = sqlite3.connect('database/data.db')
c = conn.cursor()
if not os.path.isdir('settings/'):
os.mkdir('settings/')
if not os.path.isfile('settings/vers.txt'):
ticket_create = open("settings/vers.txt", "w+")
ticket_create.write('0')
ticket_create.close()
if not os.path.isfile('settings/settings.txt'):
conf_create = open("settings/settings.txt", "w+")
command_prefix = input('What would you like your prefix to be? Leave blank for default. ')
if (command_prefix.isalpha() and len(command_prefix) == 1) or command_prefix.isdigit():
print("That prefix is too dangerous. Reverting to default")
command_prefix = "-"
conf_create.write('#Your bot token. Please follow TO GET A token directions in README.txt to get a token\n' + token
+ '#Your bot prefix\n' + command_prefix)
conf_create.close()
input('Please follow TO GET A token directions in README.txt to get a token')
sys.exit(0)
# Get all constants
num_lines = 0
with open('settings/settings.txt') as fp:
line = fp.readline()
while line:
if line[0] is not '' and line[0] is not '#':
num_lines += 1
line = fp.readline()
fp.seek(0)
line = fp.readline()
while line:
if token is '0' and line[0] is not '#' and line is not '':
token = line[:-1]
elif command_prefix is '' and line[0] is not '#' and line is not '':
command_prefix = line
line = fp.readline()
opener = urllib.request.build_opener()
opener.addheaders = [('User-agent', 'Mozilla/5.0')]
urllib.request.install_opener(opener)
if len(token) < 14:
input('Please follow TO GET A token directions in README.txt to get a token')
sys.exit(0)
bot = Bot(command_prefix=command_prefix)
@bot.event
async def on_ready():
print('Logged in as')
print(bot.user.name + ' displayed as ' + bot_name)
print(bot.user.id)
print("Prefix: " + command_prefix)
print('------')
print('Ready to work')
for server in bot.servers:
c.execute('''SELECT serverid FROM servers WHERE serverid=?''', (server.id,))
got = c.fetchone()
if got is None:
c.execute('''INSERT INTO servers(servername, serverid, prefix, ticketnumber, intro)
VALUES(?,?,?,?,?)''', (server.name, server.id, '-', 0, None))
query = '''CREATE TABLE IF NOT EXISTS {} (ticket INT PRIMARY KEY, channelid TEXT, userid TEXT, username
TEXT, content TEXT, address TEXT, requiredbtc REAL, dollaramnt REAL,
senttime TEXT, messageid TEXT)'''.format(combine_server_string(server))
c.execute(query)
c.execute('''CREATE TABLE IF NOT EXISTS Addresses (serverid TEXT,
address TEXT, balance REAL, ticket INT)''')
conn.commit()
await bot.change_presence(game=*******.Game(name="you heathens writhe", type=3))
await bot.edit_profile(username=bot_name)
if platform.system() == "Windows":
user_name = os.getenv('username')
desktop = 'C:\\Users\\' + user_name + '\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\'
if not os.path.isfile(desktop + 'mbot.ink'):
path = os.path.join(desktop, "mbot.lnk")
target = os.getcwd() + "\\main.exe"
working_dir = os.getcwd()
shell = Dispatch('WScript.Shell')
shortcut = shell.CreateShortCut(path)
shortcut.Targetpath = target
shortcut.WorkingDirectory = working_dir
shortcut.save()
threading.Timer(10, check_payments).start()
@bot.event
async def on_server_join(server):
c.execute('''INSERT INTO servers(servername, serverid, prefix, ticketnumber, intro)
VALUES(?,?,?,?,?)''', (server.name, server.id, '-', 0, None))
query = '''CREATE TABLE IF NOT EXISTS {} (ticket INT PRIMARY KEY, channelid TEXT, userid TEXT,
username TEXT, content TEXT, address TEXT, requiredbtc REAL, dollaramnt REAL,
senttime TEXT, messageid TEXT)'''.format(combine_server_string(server))
c.execute(query)
conn.commit()
@bot.event
async def on_command_error(error, ctx):
print("Error on: " + ctx.message.content)
if isinstance(error, commands.CommandNotFound):
return
else:
print(error)
@bot.command(pass_context=True)
async def ticket(ctx):
"""Opens up a new ticket channel"""
if ctx.message.channel.name == "tickets":
await bot.delete_message(ctx.message)
server = ctx.message.server
author_id = ctx.message.author.id
author_name = str(await bot.get_user_info(author_id))
c.execute('''SELECT ticketnumber FROM servers WHERE serverid=?''', (server.id,))
number = c.fetchone()[0]
c.execute('''SELECT intro FROM servers WHERE serverid=?''', (server.id,))
server_intro = c.fetchone()[0]
everyone = *******.PermissionOverwrite(read_messages=False)
mine = *******.PermissionOverwrite(read_messages=True)
test = await bot.create_channel(server, str(number), (server.default_role, everyone),
(ctx.message.author, mine))
if server_intro is not None:
try:
await bot.send_message(test, "Hello. Please copy, paste and fill out the following form.\n"
+ "```" + command_prefix + "send " + server_intro + "```")
except:
print("I couldn't send the intro to channel, sending to author")
await bot.send_message(ctx.message.author,
"Hello. Please copy, paste and fill out the following form in the ticket\n"
+ "```" + command_prefix + "send " + server_intro + "```")
c.execute('''UPDATE servers SET ticketnumber = ? WHERE serverid=? ''', (str(number+1), server.id,))
cleaned_name = re.sub("[^0-9a-zA-Z# '.!?]+", '', author_name)
query = '''INSERT INTO {} (ticket, channelid, userid, username)
VALUES(?,?,?,?)'''.format(combine_server_string(server))
c.execute(query, (str(number), str(test.id), str(author_id), cleaned_name,))
conn.commit()
else:
await bot.send_message(ctx.message.author, "Please use the correct channel labeled 'tickets'")
@bot.command(pass_context=True)
async def intro(ctx):
"""Adds/ updates the ticket channel welcome message"""
if ctx.message.author.server_permissions.administrator:
message_content = ctx.message.content[len(command_prefix)+6:]
c.execute('''UPDATE servers SET intro = ? WHERE serverid = ? ''',
(message_content, ctx.message.server.id,))
conn.commit()
await bot.say("Updated.")
await bot.delete_message(ctx.message)
@bot.command(pass_context=True)
async def prefix(ctx):
"""Changes the prefix to enact the bot"""
if ctx.message.author.server_permissions.administrator:
new_prefix = (ctx.message.content[len(command_prefix)+7:])
server_id = ctx.message.server.id
if (new_prefix.isalpha() and len(new_prefix) == 1) or new_prefix.isdigit():
await bot.send_message(ctx.message.channel, "That prefix is too dangerous. Please choose another.")
else:
c.execute('''UPDATE servers SET ticketnumber = ? WHERE serverid = ? ''', (new_prefix, server_id,))
conn.commit()
await bot.send_message(ctx.message.channel, "My future prefix will be: " + new_prefix)
else:
await bot.say("You do not have permissions for that.")
await bot.delete_message(ctx.message)
@bot.command(pass_context=True)
async def send(ctx):
"""Stores local copy of ticket data. Can also append new data to an old ticket"""
await bot.delete_message(ctx.message)
server = ctx.message.server
query = '''SELECT content FROM {} WHERE channelid = ?'''.format(combine_server_string(server))
c.execute(query, (ctx.message.channel.id,))
old_content = c.fetchone()
if old_content is None:
await bot.say("Channel is not in my database.")
return
else:
if old_content[0] is not None:
new_content = old_content[0] + "Update at: " + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + "\n" \
+ ctx.message.content[len(command_prefix)+5:] + "\n"
else:
new_content = ctx.message.content[len(command_prefix) + 5:] + '\n'
query = '''UPDATE {} SET content = ? WHERE channelid = ?'''.format(combine_server_string(server))
c.execute(query, (new_content, ctx.message.channel.id,))
conn.commit()
await bot.say(ctx.message.author.display_name + " I have received your message.")
@bot.command(pass_context=True)
async def close(ctx):
"""Deletes the channel and local ticket data"""
if ctx.message.author.server_permissions.administrator:
query = '''SELECT FROM {} WHERE channelid = ?'''.format(combine_server_string(ctx.message.server))
c.execute(query, (ctx.message.channel.id,))
check_exist = c.fetchone()
if check_exist is not None:
query = ''''DELETE FROM {} WHERE channelid=?'''.format(combine_server_string(ctx.message.server))
c.execute(query, (ctx.message.channel.id,))
await bot.delete_channel(ctx.message.channel)
else:
await bot.say("Channel not in database.")
await bot.delete_message(ctx.message)
@bot.command(pass_context=True)
async def console(ctx):
"""Posts ticket data to the local console"""
if ctx.message.author.server_permissions.administrator:
await bot.delete_message(ctx.message)
query = '''SELECT content FROM {} WHERE channelid = ?'''.format(combine_server_string(ctx.message.server))
c.execute(query, (ctx.message.channel.id,))
check_exist = c.fetchone()
if check_exist is not None:
print(check_exist[0])
else:
print("No data")
@bot.command(pass_context=True)
async def post(ctx):
"""Posts ticket data to the channel"""
if ctx.message.author.server_permissions.administrator:
await bot.delete_message(ctx.message)
query = '''SELECT content FROM {} WHERE channelid = ?'''.format(combine_server_string(ctx.message.server))
c.execute(query, (ctx.message.channel.id,))
check_exist = c.fetchone()
if check_exist is not None:
await bot.say('```' + check_exist[0] + '```')
else:
await bot.say('No data')
@bot.command(pass_context=True)
async def private(ctx):
"""Pms ticket data to the admin"""
if ctx.message.author.server_permissions.administrator:
await bot.delete_message(ctx.message)
query = '''SELECT content FROM {} WHERE channelid = ?'''.format(combine_server_string(ctx.message.server))
c.execute(query, (ctx.message.channel.id,))
check_exist = c.fetchone()
if check_exist is not None:
await bot.send_message(ctx.message.author, '```' + check_exist[0] + '```')
else:
await bot.say('No data')
@bot.command(pass_context=True)
async def ids(ctx):
"""Returns a CSV file of all users on the server. May take some time."""
if ctx.message.author.server_permissions.administrator:
await bot.request_offline_members(ctx.message.server)
print("This may take a while.")
before = time.time()
nicknames = [m.id for m in ctx.message.server.members]
with open('ids.csv', mode='w', encoding='utf-8', newline='') as f:
writer = csv.writer(f, dialect='excel')
for v in nicknames:
writer.writerow([await bot.get_user_info(v)])
after = time.time()
await bot.send_file(ctx.message.author, 'ids.csv', filename='ids.csv',
content="Also saved locally in ids.csv. Compiled in {:.4}ms.".format((after - before)*1000))
@bot.command(pass_context=True)
async def sendids(ctx):
"""Sends a pm to all users on previously generated ids.txt"""
if ctx.message.author.server_permissions.administrator:
if os.path.isfile('ids.txt'):
f = open('ids.txt')
id_line = f.readline()
while line:
recip = await bot.get_user_info(id_line)
try:
time.sleep(.1)
await bot.send_message(recip, ctx.message.content[len(command_prefix)+8:])
except:
print('Can not send to ' + recip)
id_line = f.readline()
f.close()
else:
bot.send_message(ctx.message.author, "You haven't generated an ids.txt file. Run ```" + command_prefix
+ 'textids```')
print("You haven't generated an ids.txt file. Run '''" + command_prefix + 'textids```')
@bot.command(pass_context=True)
async def textids(ctx):
"""Generate a local text file of ids"""
if ctx.message.author.server_permissions.administrator:
await bot.request_offline_members(ctx.message.server)
id_numbers = [m.id for m in ctx.message.server.members]
all_ids = ''
for v in id_numbers:
if all_ids == '':
all_ids = v
else:
all_ids = all_ids + '\n' + v
f = open('ids.txt', "w")
f.write(all_ids)
f.close()
@bot.event
async def on_message(ctx):
if ctx.channel.name == 'tickets' and not ctx.author.server_permissions.administrator and not\
ct*****ntent == command_prefix + 'ticket':
await bot.send_message(ctx.author, "You may only post " + command_prefix + 'ticket in the ticket channel')
await bot.delete_message(ctx)
else:
await bot.process_commands(ctx)
@bot.command(pass_context=True)
async def done(ctx):
"""Work in progress system to automatically accept btc"""
if ctx.message.author.server_permissions.administrator:
server = ctx.message.server
c.execute('''SELECT address FROM Addresses WHERE (serverid=? and
ticket IS NULL)''', (server.id,))
addr = c.fetchone()
if addr is not None:
dollars_due = float(re.sub("[^0-9.]+", '', ctx.message.content[len(command_prefix)+5:]))
amount_due = dollars_to_btc(dollars_due)
msg = await bot.say('Please send ' + str(amount_due) + ' bitcoin to ' + addr[0] + ' within 60 minutes')
query = '''UPDATE {} SET address=?, requiredbtc=?, dollaramnt=?, senttime=?,
messageid=? WHERE channelid=?'''.format(combine_server_string(server))
sent_time = time.time()
msg_id = msg.id
c.execute(query, (addr[0], amount_due, dollars_due, sent_time, msg_id, ctx.message.channel.id))
query = '''SELECT ticket FROM {} WHERE channelid = ?'''.format(combine_server_string(ctx.message.server))
c.execute(query, (ctx.message.channel.id,))
ticket_number = c.fetchone()[0]
c.execute('''UPDATE Addresses SET ticket=? WHERE address=?''', (ticket_number, addr[0]))
conn.commit()
else:
await bot.say('Please generate some (more?) addresses.')
@bot.command(pass_context=True)
async def seed(ctx):
"""Add 50 receiving addresses to the database. RUN ONCE"""
await bot.delete_message(ctx.message)
if ctx.message.author is ctx.message.server.owner:
c.execute('''SELECT address FROM Addresses WHERE serverid=?''', (ctx.message.server.id,))
already_ran = c.fetchone()
if already_ran is None:
server = ctx.message.server
server_id = server.id
k = keystore.from_seed(ctx.message.content[len(command_prefix) + 5:], '', False)
for x in range(0, 49):
addr = bitcoin.pubkey_to_address('p2pkh', k.derive_pubkey(False, x))
addr_balance = get_balance(addr)
c.execute('''INSERT INTO Addresses (serverid, address, balance)
VALUES(?,?,?)''', (server_id, addr, addr_balance,))
conn.commit()
await bot.send_message(ctx.message.author, "50 btc addresses have been generated and stored.")
else:
await bot.say("Addresses already generated.")
async def delete_channel(channel_id):
await bot.delete_channel(channel_id)
async def update_payment(message_id, channel_id, new_btc, address, author_id, server_id):
await bot.http.delete_message(channel_id, message_id, server_id)
await bot.http.edit_message(message_id, channel_id, "<@" + author_id + "> Please send the updated amount of " +
new_btc + " to " + address, server_id)
await bot.http.send_message(channel_id, "<@" + author_id + ">", server_id)
async def payment_time_left(channel_id, message_id, amount_due, address, sent_time):
time_left = 60 - round((time.time() - float(sent_time)) / 60)
await bot.http.edit_message(message_id, channel_id, 'Please send ' + str(amount_due) + ' bitcoin to ' + address +
' within ' + str(time_left) + ' minutes')
def combine_server_string(server):
return '[' + clean_string(server.name) + ":" + server.id + ']'
def get_balance(address):
response = requests.get("https://bitaps.com/api/address/" + address)
if response.status_code != 200:
return None
balance = round(float(response.json()['confirmed_balance'] / 100000000), 5)
return balance
def dollars_to_btc(dollars_due):
bitcoin_api_url = 'https://api.coinmarketcap.com/v1/ticker/bitcoin/'
response = requests.get(bitcoin_api_url)
response_json = response.json()
btc_price = float(response_json[0]['price_usd'])
return round(dollars_due / btc_price, 4)
def clean_string(string):
cleaned_string = re.sub("[^0-9a-zA-Z ']+", '', string)
return cleaned_string
def check_payments():
conn2 = sqlite3.connect('database/data.db')
c2 = conn2.cursor()
for server in bot.servers:
query = '''SELECT * FROM {} WHERE requiredbtc IS NOT NULL'''.format(combine_server_string(server))
required = c2.execute(query)
for k in required:
channel_id = k[1]
addr = k[5]
required_btc = k[6]
dollars_due = k[7]
sent_time = k[8]
message_id = k[9]
c2.execute('''SELECT balance FROM Addresses WHERE address=?''', (addr,))
start_balance = c2.fetchone()[0]
if (float(start_balance) + float(get_balance(addr))) > float(required_btc):
print("Ticket paid")
query = '''DELETE FROM {} WHERE address=?'''.format(combine_server_string(server))
c2.execute(query, (addr,))
asyncio.run_coroutine_threadsafe(delete_channel(channel_id), loop)
elif float(time.time()) > float(sent_time) + 3600:
print("Ticket out of time")
new_btc = dollars_to_btc(dollars_due)
asyncio.run_coroutine_threadsafe(update_payment(message_id, channel_id, new_btc, addr, k[3],
server.id), loop)
query = '''UPDATE {} SET requiredbtc=?, senttime=?
WHERE channelid=?'''.format(combine_server_string(server))
c2.execute(query, (new_btc, time.time(), channel_id))
else:
print("Updating ticket time left")
asyncio.run_coroutine_threadsafe(payment_time_left(channel_id, message_id,
required_btc, addr, sent_time), loop)
threading.Timer(60, check_payments).start()
if __name__ == '__main__':
bot.run(token)