Merge pull request #20 from ShootingKing-AM/feature-topgg

New module Topgg
This commit is contained in:
Interitio
2022-01-19 13:57:07 +02:00
committed by GitHub
22 changed files with 564 additions and 23 deletions

48
bot/LionContext.py Normal file
View File

@@ -0,0 +1,48 @@
import datetime
import discord
from cmdClient import Context
from cmdClient.logger import log
reply_callbacks: list = [] # TODO Extend to all cmdClient.Context.Utils to give flexibility to modules
class LionContext(Context):
"""
Subclass to allow easy attachment of custom hooks and structure to contexts.
"""
def __init__(self, client, **kwargs):
super().__init__(client, **kwargs)
@classmethod
def util(self, util_func):
"""
Decorator to make a utility function available as a Context instance method
"""
log('added util_function: ' + util_func.__name__)
def util_fun_wrapper(*args, **kwargs):
[args, kwargs] = self.util_pre(util_func, *args, **kwargs)
return util_func(*args, **kwargs)
util_fun_wrapper.__name__ = util_func.__name__ # Hack
super().util(util_fun_wrapper)
@classmethod
def util_pre(self, util_func, *args, **kwargs):
if util_func.__name__ == 'reply':
for cb in reply_callbacks:
[args, kwargs] = cb(util_func, *args, **kwargs) # Nesting handlers. Note: args and kwargs are mutable
return [args, kwargs]
def register_reply_callback(func):
reply_callbacks.append(func)
def unregister_reply_callback(func):
reply_callbacks.remove(func)

View File

@@ -14,7 +14,7 @@ meta = RowTable(
user_config = RowTable( user_config = RowTable(
'user_config', 'user_config',
('userid', 'timezone'), ('userid', 'timezone', 'remaind_upvote'),
'userid', 'userid',
cache=TTLCache(5000, ttl=60*5) cache=TTLCache(5000, ttl=60*5)
) )

View File

@@ -5,6 +5,8 @@ from meta import client
from data import tables as tb from data import tables as tb
from settings import UserSettings, GuildSettings from settings import UserSettings, GuildSettings
# Give modules the ability to intercept addCoin() calls
_lion_add_coins_callbacks: list = []
class Lion: class Lion:
""" """
@@ -214,10 +216,13 @@ class Lion:
timezone = self.settings.timezone.value timezone = self.settings.timezone.value
return naive_utc_dt.replace(tzinfo=pytz.UTC).astimezone(timezone) return naive_utc_dt.replace(tzinfo=pytz.UTC).astimezone(timezone)
def addCoins(self, amount, flush=True): def addCoins(self, amount, flush=True, ignorebonus=False):
""" """
Add coins to the user, optionally store the transaction in pending. Add coins to the user, optionally store the transaction in pending.
""" """
for cb in _lion_add_coins_callbacks:
[self, amount, flush, ignorebonus] = cb(self, amount, flush, ignorebonus)
self._pending_coins += amount self._pending_coins += amount
self._pending[self.key] = self self._pending[self.key] = self
if flush: if flush:
@@ -251,3 +256,12 @@ class Lion:
for lion in lions: for lion in lions:
lion._pending_coins -= int(lion._pending_coins) lion._pending_coins -= int(lion._pending_coins)
cls._pending.pop(lion.key, None) cls._pending.pop(lion.key, None)
# TODO Expand this callback system to other functions
# Note: callbacks MUST return [self, amount, flush, ignorebonus] modified/unmodified
def register_addcoins_callback(func):
_lion_add_coins_callbacks.append(func)
def unregister_addcoins_callback(func):
_lion_add_coins_callbacks.remove(func)

View File

@@ -69,6 +69,8 @@ def _format_selectkeys(keys):
""" """
if not keys: if not keys:
return "*" return "*"
elif type(keys) is str:
return keys
else: else:
return ", ".join(keys) return ", ".join(keys)

View File

@@ -3,7 +3,7 @@ from cmdClient.cmdClient import cmdClient
from .config import conf from .config import conf
from .sharding import shard_number, shard_count from .sharding import shard_number, shard_count
from LionContext import LionContext
# Initialise client # Initialise client
owners = [int(owner) for owner in conf.bot.getlist('owners')] owners = [int(owner) for owner in conf.bot.getlist('owners')]
@@ -14,6 +14,7 @@ client = cmdClient(
owners=owners, owners=owners,
intents=intents, intents=intents,
shard_id=shard_number, shard_id=shard_number,
shard_count=shard_count shard_count=shard_count,
baseContext=LionContext
) )
client.conf = conf client.conf = conf

View File

@@ -7,6 +7,7 @@ from .stats import *
from .user_config import * from .user_config import *
from .workout import * from .workout import *
from .todo import * from .todo import *
from .topgg import *
from .reminders import * from .reminders import *
from .renting import * from .renting import *
from .moderation import * from .moderation import *

View File

@@ -60,7 +60,7 @@ async def cmd_send(ctx):
return await ctx.embed_reply("We are still waiting for {} to open an account.".format(target.mention)) return await ctx.embed_reply("We are still waiting for {} to open an account.".format(target.mention))
# Finally, send the amount and the ack message # Finally, send the amount and the ack message
target_lion.addCoins(amount) target_lion.addCoins(amount, ignorebonus=True)
source_lion.addCoins(-amount) source_lion.addCoins(-amount)
embed = discord.Embed( embed = discord.Embed(

View File

@@ -61,10 +61,10 @@ async def cmd_set(ctx):
# Postgres `coins` column is `integer`, sanity check postgres int limits - which are smalled than python int range # Postgres `coins` column is `integer`, sanity check postgres int limits - which are smalled than python int range
target_coins_to_set = target_lion.coins + amount target_coins_to_set = target_lion.coins + amount
if target_coins_to_set >= 0 and target_coins_to_set <= POSTGRES_INT_MAX: if target_coins_to_set >= 0 and target_coins_to_set <= POSTGRES_INT_MAX:
target_lion.addCoins(amount) target_lion.addCoins(amount, ignorebonus=True)
elif target_coins_to_set < 0: elif target_coins_to_set < 0:
target_coins_to_set = -target_lion.coins # Coins cannot go -ve, cap to 0 target_coins_to_set = -target_lion.coins # Coins cannot go -ve, cap to 0
target_lion.addCoins(target_coins_to_set) target_lion.addCoins(target_coins_to_set, ignorebonus=True)
target_coins_to_set = 0 target_coins_to_set = 0
else: else:
return await ctx.embed_reply("Member coins cannot be more than {}".format(POSTGRES_INT_MAX)) return await ctx.embed_reply("Member coins cannot be more than {}".format(POSTGRES_INT_MAX))

View File

@@ -1,4 +1,5 @@
import asyncio import asyncio
from codecs import ignore_errors
import logging import logging
import traceback import traceback
import datetime import datetime
@@ -500,7 +501,7 @@ class ReactionRoleMessage:
if price and refund: if price and refund:
# Give the user the refund # Give the user the refund
lion = Lion.fetch(self.guild.id, member.id) lion = Lion.fetch(self.guild.id, member.id)
lion.addCoins(price) lion.addCoins(price, ignorebonus=True)
# Notify the user # Notify the user
embed = discord.Embed( embed = discord.Embed(

View File

@@ -1,8 +1,8 @@
from data import RowTable from data.interfaces import RowTable
reminders = RowTable( reminders = RowTable(
'reminders', 'reminders',
('reminderid', 'userid', 'remind_at', 'content', 'message_link', 'interval', 'created_at'), ('reminderid', 'userid', 'remind_at', 'content', 'message_link', 'interval', 'created_at', 'title', 'footer'),
'reminderid' 'reminderid'
) )

View File

@@ -150,11 +150,13 @@ class Reminder:
# Build the message embed # Build the message embed
embed = discord.Embed( embed = discord.Embed(
title="You asked me to remind you!", title="You asked me to remind you!" if self.data.title is None else self.data.title,
colour=discord.Colour.orange(), colour=discord.Colour.orange(),
description=self.data.content, description=self.data.content,
timestamp=datetime.datetime.utcnow() timestamp=datetime.datetime.utcnow()
) )
if self.data.message_link:
embed.add_field(name="Context?", value="[Click here]({})".format(self.data.message_link)) embed.add_field(name="Context?", value="[Click here]({})".format(self.data.message_link))
if self.data.interval: if self.data.interval:
@@ -165,6 +167,9 @@ class Reminder:
) )
) )
if self.data.footer:
embed.set_footer(text=self.data.footer)
# Update the reminder data, and reschedule if required # Update the reminder data, and reschedule if required
if self.data.interval: if self.data.interval:
next_time = self.data.remind_at + datetime.timedelta(seconds=self.data.interval) next_time = self.data.remind_at + datetime.timedelta(seconds=self.data.interval)

View File

@@ -0,0 +1,6 @@
from .module import module
from . import webhook
from . import commands
from . import data
from . import settings

View File

@@ -0,0 +1,76 @@
import discord
from .module import module
from wards import guild_admin
from bot.cmdClient.checks.global_perms import in_guild
from settings.user_settings import UserSettings
from .webhook import on_dbl_vote
from .utils import *
@module.cmd(
"forcevote",
desc="Simulate Topgg Vote.",
group="Guild Admin",
aliases=('debugvote', 'topggvote')
)
@guild_admin()
async def cmd_forcevote(ctx):
"""
Usage``:
{prefix}forcevote
Description:
Simulate Topgg Vote without actually a confirmation from Topgg site.
Can be used for force a vote for testing or if topgg has an error or production time bot error.
"""
target = ctx.author
# Identify the target
if ctx.args:
if not ctx.msg.mentions:
return await ctx.error_reply("Please mention a user to simulate a vote!")
target = ctx.msg.mentions[0]
await on_dbl_vote({"user": target.id, "type": "test"})
return await ctx.reply('Topgg vote simulation successful on {}'.format(target))
@module.cmd(
"vote",
desc="Get top.gg boost for 25% more LCs.",
group="Economy",
aliases=('topgg', 'topggvote', 'upvote')
)
@in_guild()
async def cmd_vote(ctx):
"""
Usage``:
{prefix}vote
Description:
Get Top.gg bot's link for +25% Economy boost.
"""
target = ctx.author
embed=discord.Embed(
title="Claim your boost!",
description='Please click [here](https://top.gg/bot/889078613817831495/vote) vote and support our bot!\n\nThank you! {}.'.format(lion_loveemote),
colour=discord.Colour.orange()
).set_thumbnail(
url="https://cdn.discordapp.com/attachments/908283085999706153/933012309532614666/lion-love.png"
)
return await ctx.reply(embed=embed)
@module.cmd(
"vote_reminder",
group="Personal Settings",
desc="Turn on/off boost reminders."
)
async def cmd_remind_vote(ctx):
"""
Usage:
`{prefix}vote_reminder on`
`{prefix}vote_reminder off`
Enable or disable DM boost reminders.
"""
await UserSettings.settings.vote_remainder.command(ctx, ctx.author.id)

View File

@@ -0,0 +1,8 @@
from data.interfaces import RowTable
topggvotes = RowTable(
'topgg',
('voteid', 'userid', 'boostedTimestamp'),
'voteid'
)

View File

@@ -0,0 +1,68 @@
from multiprocessing import context
from LionModule import LionModule
from LionContext import register_reply_callback, unregister_reply_callback
from bot.data.conditions import NOT
from meta.client import client
from core.lion import register_addcoins_callback, unregister_addcoins_callback
from .utils import *
from .webhook import init_webhook
module = LionModule("Topgg")
upvote_info = "You have a boost available {}, to support our project and earn **25% more LionCoins** type `{}vote` {}"
@module.launch_task
async def register_hook(client):
init_webhook()
register_reply_callback(reply)
register_addcoins_callback(cb_addCoins)
client.log("Registered LionContext reply util hook.", context="Topgg" )
@module.unload_task
async def unregister_hook(client):
unregister_reply_callback(reply)
unregister_addcoins_callback(cb_addCoins)
client.log("Unregistered LionContext reply util hook.", context="Topgg" )
def reply(util_func, *args, **kwargs):
# *args will have LionContext
# **kwargs should have the actual reply() call's extra arguments
if not get_last_voted_timestamp(args[0].author.id):
args = list(args)
upvote_info_formatted = upvote_info.format(lion_yayemote, args[0].best_prefix, lion_loveemote)
if 'embed' in kwargs:
kwargs['embed'].add_field(
name="\u200b",
value=(
upvote_info_formatted
),
inline=False
)
elif 'content' in args and args['content']:
args['content'] += '\n\n' + upvote_info_formatted
elif len(args) > 1:
args[1] += '\n\n' + upvote_info_formatted
else:
args['content'] = '\n\n' + upvote_info_formatted
args = tuple(args)
return [args, kwargs]
def cb_addCoins(self, amount, flush, ignorebonus):
client.log('cb_addCoins hook with amount={} ignorebonux={}'.format(amount, ignorebonus), context='Topgg')
if not ignorebonus and amount > 0 and get_last_voted_timestamp(self.userid):
amount *= 1.25
client.log('cb_addCoins with bonus={}'.format(amount), context='Topgg')
return [self, amount, flush, ignorebonus]

View File

@@ -0,0 +1,45 @@
from settings.user_settings import UserSettings, UserSetting
from settings.setting_types import Boolean
from modules.reminders.reminder import Reminder
from modules.reminders.data import reminders
from .utils import *
@UserSettings.attach_setting
class topgg_vote_remainder(Boolean, UserSetting):
attr_name = 'vote_remainder'
_data_column = 'remaind_upvote'
_default = True
display_name = 'Upvote Reminder'
desc = "Turn on/off DM Reminders to Upvote me."
long_desc = ("Enable or disable DM boost reminders.",)
@property
def success_response(self):
if self.value:
# Check if reminder is already running
create_remainder(self.id)
return (
" I will send you boost reminders.\n\n"
"`Please make sure your DMs are open.`"
)
else:
# Check if reminder is already running and get its id
r = reminders.select_one_where(
userid=self.id,
select_columns='reminderid',
content=remainder_content,
_extra="ORDER BY remind_at DESC LIMIT 1"
)
# Cancel and delete Remainder if already running
if r:
Reminder.delete(r['reminderid'])
return (
" I won't send you boost reminders."
)

View File

@@ -0,0 +1,79 @@
from email.mime import image
import discord
import datetime
from meta.client import client
from bot.settings.setting_types import Integer
from meta import sharding
from modules.reminders.reminder import Reminder
from modules.reminders.data import reminders
from . import data as db
from data.conditions import GEQ
topgg_upvote_link = 'https://top.gg/bot/889078613817831495/vote'
remainder_content = "You can now vote again on top.gg!\nClick [here]({}) to vote, thank you for the support!".format(topgg_upvote_link)
lion_loveemote = '<:lionloveemote:933003977656795136>'
lion_yayemote = '<:lionyayemote:933003929229352990>'
# Will return None if user has not voted in [-12.5hrs till now]
# else will return a Tuple containing timestamp of when exactly she voted
def get_last_voted_timestamp(userid: Integer):
return db.topggvotes.select_one_where(
userid=userid,
select_columns="boostedTimestamp",
boostedTimestamp=GEQ(datetime.datetime.utcnow() - datetime.timedelta(hours=12.5)),
_extra="ORDER BY boostedTimestamp DESC LIMIT 1"
)
# Checks if a remainder is already running (immaterial of remind_at time)
# If no remainder exists creates a new remainder and schedules it
def create_remainder(userid):
if not reminders.select_one_where(
userid=userid,
content=remainder_content,
_extra="ORDER BY remind_at DESC LIMIT 1"
):
last_vote_time = get_last_voted_timestamp(userid)
# if no, Create reminder
reminder = Reminder.create(
userid=userid,
# TODO using content as a selector is not a good method
content=remainder_content,
message_link=None,
interval=None,
title="Your boost is now available! {}".format(lion_yayemote),
footer="Use `{}vote_reminder off` to stop receiving reminders.".format(client.prefix),
remind_at=last_vote_time[0] + datetime.timedelta(hours=12.5) if last_vote_time else datetime.datetime.utcnow() + datetime.timedelta(minutes=5)
# remind_at=datetime.datetime.utcnow() + datetime.timedelta(minutes=2)
)
# Schedule reminder
if sharding.shard_number == 0:
reminder.schedule()
async def send_user_dm(userid):
# Send the message, if possible
if not (user := client.get_user(userid)):
try:
user = await client.fetch_user(userid)
except discord.HTTPException:
pass
if user:
try:
embed=discord.Embed(
title="Thank you for supporting our bot on Top.gg! {}".format(lion_yayemote),
description="By voting every 12 hours you will allow us to reach and help even more students all over the world.\n \
Thank you for supporting us, enjoy your LionCoins boost!",
colour=discord.Colour.orange()
).set_image(
url="https://cdn.discordapp.com/attachments/908283085999706153/932737228440993822/lion-yay.png"
)
await user.send(embed=embed)
except discord.HTTPException:
# Nothing we can really do here. Maybe tell the user about their reminder next time?
pass

View File

@@ -0,0 +1,36 @@
from meta.client import client
from settings.user_settings import UserSettings
from utils.lib import utc_now
from meta.config import conf
import topgg
from .utils import *
@client.event
async def on_dbl_vote(data):
"""An event that is called whenever someone votes for the bot on Top.gg."""
client.log(f"Received a vote: \n{data}", context='Topgg')
db.topggvotes.insert(
userid=data['user'],
boostedTimestamp = utc_now()
)
await send_user_dm(data['user'])
if UserSettings.settings.vote_remainder.value:
create_remainder(data['user'])
if data["type"] == "test":
return client.dispatch("dbl_test", data)
@client.event
async def on_dbl_test(data):
"""An event that is called whenever someone tests the webhook system for your bot on Top.gg."""
client.log(f"Received a test vote:\n{data}", context='Topgg')
def init_webhook():
client.topgg_webhook = topgg.WebhookManager(client).dbl_webhook(conf.bot.get("topgg_route"), conf.bot.get("topgg_password"))
client.topgg_webhook.run(conf.bot.get("topgg_port"))

View File

@@ -1,8 +1,10 @@
import asyncio import asyncio
import discord import discord
from cmdClient import Context from LionContext import LionContext
from cmdClient.lib import UserCancelled, ResponseTimedOut from cmdClient.lib import UserCancelled, ResponseTimedOut
import datetime
from cmdClient import lib
from .lib import paginate_list from .lib import paginate_list
# TODO: Interactive locks # TODO: Interactive locks
@@ -19,7 +21,7 @@ async def discord_shield(coro):
pass pass
@Context.util @LionContext.util
async def cancellable(ctx, msg, add_reaction=True, cancel_message=None, timeout=300): async def cancellable(ctx, msg, add_reaction=True, cancel_message=None, timeout=300):
""" """
Add a cancellation reaction to the given message. Add a cancellation reaction to the given message.
@@ -62,7 +64,7 @@ async def cancellable(ctx, msg, add_reaction=True, cancel_message=None, timeout=
return task return task
@Context.util @LionContext.util
async def listen_for(ctx, allowed_input=None, timeout=120, lower=True, check=None): async def listen_for(ctx, allowed_input=None, timeout=120, lower=True, check=None):
""" """
Listen for a one of a particular set of input strings, Listen for a one of a particular set of input strings,
@@ -114,7 +116,7 @@ async def listen_for(ctx, allowed_input=None, timeout=120, lower=True, check=Non
return message return message
@Context.util @LionContext.util
async def selector(ctx, header, select_from, timeout=120, max_len=20): async def selector(ctx, header, select_from, timeout=120, max_len=20):
""" """
Interactive routine to prompt the `ctx.author` to select an item from a list. Interactive routine to prompt the `ctx.author` to select an item from a list.
@@ -214,7 +216,7 @@ async def selector(ctx, header, select_from, timeout=120, max_len=20):
return result return result
@Context.util @LionContext.util
async def pager(ctx, pages, locked=True, start_at=0, add_cancel=False, **kwargs): async def pager(ctx, pages, locked=True, start_at=0, add_cancel=False, **kwargs):
""" """
Shows the user each page from the provided list `pages` one at a time, Shows the user each page from the provided list `pages` one at a time,
@@ -371,7 +373,7 @@ async def _pager(ctx, out_msg, pages, locked, start_at, add_cancel, **kwargs):
pass pass
@Context.util @LionContext.util
async def input(ctx, msg="", timeout=120): async def input(ctx, msg="", timeout=120):
""" """
Listen for a response in the current channel, from ctx.author. Listen for a response in the current channel, from ctx.author.
@@ -413,7 +415,7 @@ async def input(ctx, msg="", timeout=120):
return result return result
@Context.util @LionContext.util
async def ask(ctx, msg, timeout=30, use_msg=None, del_on_timeout=False): async def ask(ctx, msg, timeout=30, use_msg=None, del_on_timeout=False):
""" """
Ask ctx.author a yes/no question. Ask ctx.author a yes/no question.
@@ -459,3 +461,41 @@ async def ask(ctx, msg, timeout=30, use_msg=None, del_on_timeout=False):
if result in ["n", "no"]: if result in ["n", "no"]:
return 0 return 0
return 1 return 1
# this reply() will be overide baseContext's reply with LionContext's, whcih can
# hook pre_execution of any util.
# Using this system, Module now have much power to change Context's utils
@LionContext.util
async def reply(ctx, content=None, allow_everyone=False, **kwargs):
"""
Helper function to reply in the current channel.
"""
if not allow_everyone:
if content:
content = lib.sterilise_content(content)
message = await ctx.ch.send(content=content, **kwargs)
ctx.sent_messages.append(message)
return message
# this reply() will be overide baseContext's reply
@LionContext.util
async def error_reply(ctx, error_str):
"""
Notify the user of a user level error.
Typically, this will occur in a red embed, posted in the command channel.
"""
embed = discord.Embed(
colour=discord.Colour.red(),
description=error_str,
timestamp=datetime.datetime.utcnow()
)
try:
message = await ctx.ch.send(embed=embed)
ctx.sent_messages.append(message)
return message
except discord.Forbidden:
message = await ctx.reply(error_str)
ctx.sent_messages.append(message)
return message

View File

@@ -14,3 +14,7 @@ data_appid = LionBot
shard_count = 1 shard_count = 1
lion_sync_period = 60 lion_sync_period = 60
topgg_password =
topgg_route =
topgg_port =

View File

@@ -0,0 +1,76 @@
ALTER TABLE user_config
ADD COLUMN remaind_upvote BOOLEAN DEFAULT TRUE
ALTER TABLE reminders
ADD COLUMN title TEXT DEFAULT NULL,
ADD COLUMN footer TEXT DEFAULT NULL
-- Topgg Data {{{
CREATE TABLE IF NOT EXISTS topgg(
voteid SERIAL PRIMARY KEY,
userid BIGINT NOT NULL,
boostedTimestamp TIMESTAMPTZ NOT NULL
);
CREATE INDEX topgg_member ON topgg (userid);
-- }}}
DROP FUNCTION close_study_session(_guildid BIGINT, _userid BIGINT);
CREATE FUNCTION close_study_session(_guildid BIGINT, _userid BIGINT)
RETURNS SETOF members
AS $$
BEGIN
RETURN QUERY
WITH
current_sesh AS (
DELETE FROM current_sessions
WHERE guildid=_guildid AND userid=_userid
RETURNING
*,
EXTRACT(EPOCH FROM (NOW() - start_time)) AS total_duration,
stream_duration + COALESCE(EXTRACT(EPOCH FROM (NOW() - stream_start)), 0) AS total_stream_duration,
video_duration + COALESCE(EXTRACT(EPOCH FROM (NOW() - video_start)), 0) AS total_video_duration,
live_duration + COALESCE(EXTRACT(EPOCH FROM (NOW() - live_start)), 0) AS total_live_duration
), bonus_userid AS (
SELECT COUNT(boostedTimestamp),
CASE WHEN EXISTS (
SELECT 1 FROM Topgg
WHERE Topgg.userid=_userid AND EXTRACT(EPOCH FROM (NOW() - boostedTimestamp)) < 12.5*60*60
) THEN
(array_agg(
CASE WHEN boostedTimestamp <= current_sesh.start_time THEN
1.25
ELSE
(((current_sesh.total_duration - EXTRACT(EPOCH FROM (boostedTimestamp - current_sesh.start_time)))/current_sesh.total_duration)*0.25)+1
END))[1]
ELSE
1
END
AS bonus
FROM Topgg, current_sesh
WHERE Topgg.userid=_userid AND EXTRACT(EPOCH FROM (NOW() - boostedTimestamp)) < 12.5*60*60
ORDER BY (array_agg(boostedTimestamp))[1] DESC LIMIT 1
), saved_sesh AS (
INSERT INTO session_history (
guildid, userid, channelid, rating, tag, channel_type, start_time,
duration, stream_duration, video_duration, live_duration,
coins_earned
) SELECT
guildid, userid, channelid, rating, tag, channel_type, start_time,
total_duration, total_stream_duration, total_video_duration, total_live_duration,
((total_duration * hourly_coins + live_duration * hourly_live_coins) * bonus_userid.bonus )/ 3600
FROM current_sesh, bonus_userid
RETURNING *
)
UPDATE members
SET
tracked_time=(tracked_time + saved_sesh.duration),
coins=(coins + saved_sesh.coins_earned)
FROM saved_sesh
WHERE members.guildid=saved_sesh.guildid AND members.userid=saved_sesh.userid
RETURNING members.*;
END;
$$ LANGUAGE PLPGSQL;
-- }}}
INSERT INTO VersionHistory (version, author) VALUES (8, 'v8-v9 migration');

View File

@@ -41,7 +41,8 @@ CREATE TABLE global_guild_blacklist(
-- User configuration data {{{ -- User configuration data {{{
CREATE TABLE user_config( CREATE TABLE user_config(
userid BIGINT PRIMARY KEY, userid BIGINT PRIMARY KEY,
timezone TEXT timezone TEXT,
remaind_upvote BOOLEAN DEFAULT TRUE
); );
-- }}} -- }}}
@@ -166,7 +167,9 @@ CREATE TABLE reminders(
content TEXT NOT NULL, content TEXT NOT NULL,
message_link TEXT, message_link TEXT,
interval INTEGER, interval INTEGER,
created_at TIMESTAMP DEFAULT (now() at time zone 'utc') created_at TIMESTAMP DEFAULT (now() at time zone 'utc'),
title TEXT DEFAULT NULL,
footer TEXT DEFAULT NULL
); );
CREATE INDEX reminder_users ON reminders (userid); CREATE INDEX reminder_users ON reminders (userid);
-- }}} -- }}}
@@ -512,6 +515,25 @@ AS $$
stream_duration + COALESCE(EXTRACT(EPOCH FROM (NOW() - stream_start)), 0) AS total_stream_duration, stream_duration + COALESCE(EXTRACT(EPOCH FROM (NOW() - stream_start)), 0) AS total_stream_duration,
video_duration + COALESCE(EXTRACT(EPOCH FROM (NOW() - video_start)), 0) AS total_video_duration, video_duration + COALESCE(EXTRACT(EPOCH FROM (NOW() - video_start)), 0) AS total_video_duration,
live_duration + COALESCE(EXTRACT(EPOCH FROM (NOW() - live_start)), 0) AS total_live_duration live_duration + COALESCE(EXTRACT(EPOCH FROM (NOW() - live_start)), 0) AS total_live_duration
), bonus_userid AS (
SELECT COUNT(boostedTimestamp),
CASE WHEN EXISTS (
SELECT 1 FROM Topgg
WHERE Topgg.userid=_userid AND EXTRACT(EPOCH FROM (NOW() - boostedTimestamp)) < 12.5*60*60
) THEN
(array_agg(
CASE WHEN boostedTimestamp <= current_sesh.start_time THEN
1.25
ELSE
(((current_sesh.total_duration - EXTRACT(EPOCH FROM (boostedTimestamp - current_sesh.start_time)))/current_sesh.total_duration)*0.25)+1
END))[1]
ELSE
1
END
AS bonus
FROM Topgg, current_sesh
WHERE Topgg.userid=_userid AND EXTRACT(EPOCH FROM (NOW() - boostedTimestamp)) < 12.5*60*60
ORDER BY (array_agg(boostedTimestamp))[1] DESC LIMIT 1
), saved_sesh AS ( ), saved_sesh AS (
INSERT INTO session_history ( INSERT INTO session_history (
guildid, userid, channelid, rating, tag, channel_type, start_time, guildid, userid, channelid, rating, tag, channel_type, start_time,
@@ -520,8 +542,8 @@ AS $$
) SELECT ) SELECT
guildid, userid, channelid, rating, tag, channel_type, start_time, guildid, userid, channelid, rating, tag, channel_type, start_time,
total_duration, total_stream_duration, total_video_duration, total_live_duration, total_duration, total_stream_duration, total_video_duration, total_live_duration,
(total_duration * hourly_coins + live_duration * hourly_live_coins) / 3600 ((total_duration * hourly_coins + live_duration * hourly_live_coins) * bonus_userid.bonus )/ 3600
FROM current_sesh FROM current_sesh, bonus_userid
RETURNING * RETURNING *
) )
UPDATE members UPDATE members
@@ -766,4 +788,13 @@ create TABLE timers(
CREATE INDEX timers_guilds ON timers (guildid); CREATE INDEX timers_guilds ON timers (guildid);
-- }}} -- }}}
-- Topgg Data {{{
create TABLE topgg(
voteid SERIAL PRIMARY KEY,
userid BIGINT NOT NULL,
boostedTimestamp TIMESTAMPTZ NOT NULL
);
CREATE INDEX topgg_member ON topgg (userid);
-- }}}
-- vim: set fdm=marker: -- vim: set fdm=marker: