mirror of
https://github.com/solero/houdini.git
synced 2024-11-13 22:28:21 +00:00
Full support for expiring memberships
This commit is contained in:
parent
82a3753d0d
commit
f255ad112d
@ -119,6 +119,9 @@ if __name__ == '__main__':
|
|||||||
client_group.add_argument('-kt', '--auth-ttl', action='store', type=int, default=3000,
|
client_group.add_argument('-kt', '--auth-ttl', action='store', type=int, default=3000,
|
||||||
help='Auth key TTL (seconds)')
|
help='Auth key TTL (seconds)')
|
||||||
|
|
||||||
|
membership_group = parser.add_argument_group('membership')
|
||||||
|
membership_group.add_argument('--expire-membership', action='store_true', help='Should membership expire?')
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
args.port = args.port if args.port else 9875 if args.type == 'world' else 6112
|
args.port = args.port if args.port else 9875 if args.type == 'world' else 6112
|
||||||
|
@ -1371,13 +1371,17 @@ COMMENT ON TABLE penguin_stamp IS 'Penguin earned stamps';
|
|||||||
COMMENT ON COLUMN penguin_stamp.penguin_id IS 'Stamp penguin ID';
|
COMMENT ON COLUMN penguin_stamp.penguin_id IS 'Stamp penguin ID';
|
||||||
COMMENT ON COLUMN penguin_stamp.stamp_id IS 'Stamp ID';
|
COMMENT ON COLUMN penguin_stamp.stamp_id IS 'Stamp ID';
|
||||||
COMMENT ON COLUMN penguin_stamp.recent IS 'Is recently earned?';
|
COMMENT ON COLUMN penguin_stamp.recent IS 'Is recently earned?';
|
||||||
|
COMMENT ON COLUMN penguin_stamp.recent IS 'Is recently earned?';
|
||||||
|
|
||||||
DROP TABLE IF EXISTS penguin_membership;
|
DROP TABLE IF EXISTS penguin_membership;
|
||||||
CREATE TABLE penguin_membership (
|
CREATE TABLE penguin_membership (
|
||||||
penguin_id INT NOT NULL,
|
penguin_id INT NOT NULL,
|
||||||
start TIMESTAMP NOT NULL,
|
start TIMESTAMP NOT NULL,
|
||||||
expires TIMESTAMP NOT NULL,
|
expires TIMESTAMP DEFAULT NULL,
|
||||||
PRIMARY KEY(penguin_id, start, expires),
|
start_aware BOOLEAN DEFAULT FALSE,
|
||||||
|
expires_aware BOOLEAN DEFAULT FALSE,
|
||||||
|
expired_aware BOOLEAN DEFAULT FALSE,
|
||||||
|
PRIMARY KEY(penguin_id, start),
|
||||||
CONSTRAINT penguin_membership_ibfk_1 FOREIGN KEY (penguin_id) REFERENCES penguin (id) ON DELETE RESTRICT ON UPDATE CASCADE
|
CONSTRAINT penguin_membership_ibfk_1 FOREIGN KEY (penguin_id) REFERENCES penguin (id) ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ class PenguinStringCompiler(OrderedDict):
|
|||||||
'Y': PenguinStringCompiler.attribute_by_name('y'),
|
'Y': PenguinStringCompiler.attribute_by_name('y'),
|
||||||
'Frame': PenguinStringCompiler.attribute_by_name('frame'),
|
'Frame': PenguinStringCompiler.attribute_by_name('frame'),
|
||||||
'Member': PenguinStringCompiler.attribute_by_name('member'),
|
'Member': PenguinStringCompiler.attribute_by_name('member'),
|
||||||
'MemberDays': PenguinStringCompiler.attribute_by_name('membership_days'),
|
'MemberDays': PenguinStringCompiler.attribute_by_name('membership_days_total'),
|
||||||
'Avatar': PenguinStringCompiler.attribute_by_name('avatar'),
|
'Avatar': PenguinStringCompiler.attribute_by_name('avatar'),
|
||||||
'PenguinState': PenguinStringCompiler.attribute_by_name('penguin_state'),
|
'PenguinState': PenguinStringCompiler.attribute_by_name('penguin_state'),
|
||||||
'PartyState': PenguinStringCompiler.attribute_by_name('party_state'),
|
'PartyState': PenguinStringCompiler.attribute_by_name('party_state'),
|
||||||
|
@ -148,7 +148,10 @@ class PenguinMembership(db.Model):
|
|||||||
penguin_id = db.Column(db.ForeignKey('penguin.id', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
penguin_id = db.Column(db.ForeignKey('penguin.id', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
|
||||||
nullable=False)
|
nullable=False)
|
||||||
start = db.Column(db.DateTime, primary_key=True, nullable=False)
|
start = db.Column(db.DateTime, primary_key=True, nullable=False)
|
||||||
expires = db.Column(db.DateTime, primary_key=True, nullable=False)
|
expires = db.Column(db.DateTime)
|
||||||
|
start_aware = db.Column(db.Boolean, server_default=db.text("false"))
|
||||||
|
expires_aware = db.Column(db.Boolean, server_default=db.text("false"))
|
||||||
|
expired_aware = db.Column(db.Boolean, server_default=db.text("false"))
|
||||||
|
|
||||||
|
|
||||||
class Login(db.Model):
|
class Login(db.Model):
|
||||||
|
@ -43,7 +43,7 @@ async def handle_join_server(p, penguin_id: int, login_key: str):
|
|||||||
|
|
||||||
await p.send_xt('lp', await p.string, p.coins, int(p.safe_chat), p.egg_timer_minutes,
|
await p.send_xt('lp', await p.string, p.coins, int(p.safe_chat), p.egg_timer_minutes,
|
||||||
penguin_standard_time, p.age, 0, p.minutes_played,
|
penguin_standard_time, p.age, 0, p.minutes_played,
|
||||||
"membership_days", server_time_offset, int(p.opened_playercard),
|
p.membership_days_remain, server_time_offset, int(p.opened_playercard),
|
||||||
p.map_category, p.status_field)
|
p.map_category, p.status_field)
|
||||||
|
|
||||||
spawn = random.choice(p.server.spawn_rooms)
|
spawn = random.choice(p.server.spawn_rooms)
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
from houdini import handlers
|
from houdini import handlers
|
||||||
from houdini.converters import SeparatorConverter
|
from houdini.converters import SeparatorConverter
|
||||||
from houdini.handlers import XTPacket
|
from houdini.handlers import XTPacket
|
||||||
from houdini.data.penguin import Penguin
|
from houdini.handlers.play.navigation import handle_join_server
|
||||||
|
from houdini.data import db
|
||||||
|
from houdini.data.penguin import Penguin, PenguinMembership
|
||||||
|
from houdini.data.mail import PenguinPostcard
|
||||||
from houdini.constants import ClientType
|
from houdini.constants import ClientType
|
||||||
|
|
||||||
from aiocache import cached
|
from aiocache import cached
|
||||||
from datetime import datetime
|
from datetime import datetime, timedelta
|
||||||
import random
|
import random
|
||||||
import asyncio
|
import asyncio
|
||||||
import time
|
import time
|
||||||
@ -77,6 +80,78 @@ async def server_egg_timer(server):
|
|||||||
await p.send_error_and_disconnect(910)
|
await p.send_error_and_disconnect(910)
|
||||||
|
|
||||||
|
|
||||||
|
MemberWarningDaysToExpiry = 14
|
||||||
|
MemberWarningPostcardsVanilla = [122, 123]
|
||||||
|
MemberWarningPostcardsLegacy = [163]
|
||||||
|
MemberExpiredPostcard = 124
|
||||||
|
MemberStartPostcardVanilla = 121
|
||||||
|
MemberStartPostcardLegacy = 164
|
||||||
|
|
||||||
|
|
||||||
|
@handlers.handler(XTPacket('j', 'js'), pre_login=True, before=handle_join_server)
|
||||||
|
@handlers.allow_once
|
||||||
|
async def handle_setup_membership(p):
|
||||||
|
if not p.server.config.expire_membership or p.moderator or p.character:
|
||||||
|
p.is_member = True
|
||||||
|
p.membership_days_total = p.age
|
||||||
|
return
|
||||||
|
|
||||||
|
membership_history = PenguinMembership.query.where(PenguinMembership.penguin_id == p.id)
|
||||||
|
current_timestamp = datetime.now()
|
||||||
|
postcards = []
|
||||||
|
|
||||||
|
warning_postcards = MemberWarningPostcardsVanilla if p.is_vanilla_client else MemberWarningPostcardsLegacy
|
||||||
|
start_postcard = MemberStartPostcardVanilla if p.is_vanilla_client else MemberStartPostcardLegacy
|
||||||
|
|
||||||
|
async with db.transaction():
|
||||||
|
async for membership_record in membership_history.gino.iterate():
|
||||||
|
membership_recurring = membership_record.expires is None
|
||||||
|
membership_active = membership_recurring or membership_record.expires >= current_timestamp
|
||||||
|
|
||||||
|
if membership_record.start < current_timestamp:
|
||||||
|
if membership_active:
|
||||||
|
p.is_member = True
|
||||||
|
|
||||||
|
if not membership_recurring:
|
||||||
|
days_to_expiry = (membership_record.expires.date() - datetime.now().date()).days
|
||||||
|
p.membership_days_remain = days_to_expiry
|
||||||
|
|
||||||
|
if days_to_expiry <= MemberWarningDaysToExpiry and not membership_record.expires_aware:
|
||||||
|
postcards.append(dict(
|
||||||
|
penguin_id=p.id,
|
||||||
|
postcard_id=random.choice(warning_postcards),
|
||||||
|
send_date=membership_record.expires - timedelta(days=MemberWarningDaysToExpiry)
|
||||||
|
))
|
||||||
|
await membership_record.update(expires_aware=True).apply()
|
||||||
|
else:
|
||||||
|
if p.membership_days_remain < 0:
|
||||||
|
days_since_expiry = (membership_record.expires.date() - datetime.now().date()).days
|
||||||
|
p.membership_days_remain = min(p.membership_days_remain, days_since_expiry)
|
||||||
|
|
||||||
|
if not membership_record.expired_aware:
|
||||||
|
if p.is_vanilla_client:
|
||||||
|
postcards.append(dict(
|
||||||
|
penguin_id=p.id,
|
||||||
|
postcard_id=MemberExpiredPostcard,
|
||||||
|
send_date=membership_record.expires
|
||||||
|
))
|
||||||
|
await membership_record.update(expired_aware=True).apply()
|
||||||
|
|
||||||
|
if not membership_record.start_aware:
|
||||||
|
postcards.append(dict(
|
||||||
|
penguin_id=p.id,
|
||||||
|
postcard_id=start_postcard,
|
||||||
|
send_date=membership_record.start
|
||||||
|
))
|
||||||
|
await membership_record.update(start_aware=True).apply()
|
||||||
|
|
||||||
|
membership_end_date = current_timestamp if membership_active else membership_record.expires
|
||||||
|
p.membership_days_total += (membership_end_date - membership_record.start).days
|
||||||
|
|
||||||
|
if postcards:
|
||||||
|
await PenguinPostcard.insert().values(postcards).gino.status()
|
||||||
|
|
||||||
|
|
||||||
@handlers.handler(XTPacket('u', 'h'))
|
@handlers.handler(XTPacket('u', 'h'))
|
||||||
@handlers.cooldown(59)
|
@handlers.cooldown(59)
|
||||||
async def handle_heartbeat(p):
|
async def handle_heartbeat(p):
|
||||||
|
@ -7,9 +7,9 @@ from houdini.data import penguin
|
|||||||
class Penguin(Spheniscidae, penguin.Penguin):
|
class Penguin(Spheniscidae, penguin.Penguin):
|
||||||
|
|
||||||
__slots__ = ['x', 'y', 'frame', 'toy', 'room', 'waddle', 'table',
|
__slots__ = ['x', 'y', 'frame', 'toy', 'room', 'waddle', 'table',
|
||||||
'data', 'muted', 'login_key', 'member', 'membership_days',
|
'data', 'muted', 'login_key', 'is_member', 'membership_days_total',
|
||||||
'avatar', 'walking_puffle', 'permissions', 'active_quests',
|
'membership_days_remain', 'avatar', 'walking_puffle', 'permissions',
|
||||||
'buddy_requests', 'heartbeat', 'login_timestamp',
|
'active_quests', 'legacy_buddy_requests', 'heartbeat', 'login_timestamp',
|
||||||
'egg_timer_minutes']
|
'egg_timer_minutes']
|
||||||
|
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
@ -25,8 +25,9 @@ class Penguin(Spheniscidae, penguin.Penguin):
|
|||||||
|
|
||||||
self.login_key = None
|
self.login_key = None
|
||||||
|
|
||||||
self.member = 1
|
self.is_member = False
|
||||||
self.membership_days = 1
|
self.membership_days_total = 0
|
||||||
|
self.membership_days_remain = -1
|
||||||
|
|
||||||
self.avatar = None
|
self.avatar = None
|
||||||
self.walking_puffle = None
|
self.walking_puffle = None
|
||||||
@ -58,6 +59,10 @@ class Penguin(Spheniscidae, penguin.Penguin):
|
|||||||
def safe_name(self):
|
def safe_name(self):
|
||||||
return self.safe_nickname(self.server.config.lang)
|
return self.safe_nickname(self.server.config.lang)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def member(self):
|
||||||
|
return int(self.is_member)
|
||||||
|
|
||||||
async def join_room(self, room):
|
async def join_room(self, room):
|
||||||
await room.add_penguin(self)
|
await room.add_penguin(self)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user