Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various changes in preparation for presidential election reform #96

Merged
merged 5 commits into from
Dec 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ban.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ async function UpdateTrial(cu) {
async function HandleBanCommand(discordMessage) {
const banCourtCategory = await DiscordUtil.GetBanCourtCategoryChannel();
const banCourtChannelCount = banCourtCategory.children.cache.size;
if (banCourtChannelCount >= 50) {
if (banCourtChannelCount >= 49) {
await discordMessage.channel.send('Too many ban trials in progress. Get more votes for the ones already underway to speed them up.');
return;
}
Expand Down
22 changes: 21 additions & 1 deletion commissar-user.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ class CommissarUser {
yen,
inactivity_tax_paid_until,
ban_conviction_time,
ban_pardon_time) {
ban_pardon_time,
presidential_election_vote,
presidential_election_message_id) {
this.commissar_id = commissar_id;
this.discord_id = discord_id;
this.nickname = nickname;
Expand All @@ -50,6 +52,8 @@ class CommissarUser {
this.inactivity_tax_paid_until = inactivity_tax_paid_until;
this.ban_conviction_time = ban_conviction_time;
this.ban_pardon_time = ban_pardon_time;
this.presidential_election_vote = presidential_election_vote;
this.presidential_election_message_id = presidential_election_message_id;
}

async setDiscordId(discord_id) {
Expand Down Expand Up @@ -234,6 +238,22 @@ class CommissarUser {
await this.updateFieldInDatabase('ban_pardon_time', this.ban_pardon_time);
}

async setPresidentialElectionVote(presidential_election_vote) {
if (presidential_election_vote === this.presidential_election_vote) {
return;
}
this.presidential_election_vote = presidential_election_vote;
await this.updateFieldInDatabase('presidential_election_vote', this.presidential_election_vote);
}

async setPresidentialElectionMessageId(presidential_election_message_id) {
if (presidential_election_message_id === this.presidential_election_message_id) {
return;
}
this.presidential_election_message_id = presidential_election_message_id;
await this.updateFieldInDatabase('presidential_election_message_id', this.presidential_election_message_id);
}

async updateFieldInDatabase(fieldName, fieldValue) {
//console.log(`DB update ${fieldName} = ${fieldValue} for ${this.nickname} (ID:${this.commissar_id}).`);
const sql = `UPDATE users SET ${fieldName} = ? WHERE commissar_id = ?`;
Expand Down
4 changes: 2 additions & 2 deletions discord-util.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,8 @@ async function UpdateHarmonicCentralityChatChannel(mostCentralUsers) {
const lines = [];
let maxLength = 0;
for (const user of mostCentralUsers) {
const score = user.harmonic_centrality / 100;
const scoreString = Math.round(score).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
const score = user.harmonic_centrality / 1000;
const scoreString = Math.ceil(score).toString(); // To enable commas add .replace(/\B(?=(\d{3})+(?!\d))/g, ',');
maxLength = Math.max(scoreString.length, maxLength);
const paddedScore = scoreString.padStart(maxLength, ' ');
const name = user.getNicknameOrTitleWithInsignia();
Expand Down
2 changes: 2 additions & 0 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const RoleID = require('./role-id');
const rules = require('./rules');
const TimeTogetherStream = require('./time-together-stream');
const UserCache = require('./user-cache');
const yen = require('./yen');

// Used for streaming time matrix data to the database.
const timeTogetherStream = new TimeTogetherStream(new Clock());
Expand Down Expand Up @@ -240,6 +241,7 @@ async function FilterTimeTogetherRecordsToEnforceTimeCap(timeTogetherRecords) {
async function RoutineUpdate() {
console.log('Routine update');
startTime = new Date().getTime();
await yen.DoLottery();
await Rank.UpdateUserRanks();
await UpdateVoiceActiveMembersForMainDiscordGuild();
await huddles.ScheduleUpdate();
Expand Down
2 changes: 2 additions & 0 deletions setup-database.sql
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ CREATE TABLE users
inactivity_tax_paid_until TIMESTAMP, -- Last time this user was taxed.
ban_conviction_time TIMESTAMP, -- When this user was convicted & banned in ban court.
ban_pardon_time TIMESTAMP, -- When this user was convicted & banned in ban court.
presidential_election_vote INT, -- The commissar_id that this user is voting for in the presidential election. NULL if has not voted.
presidential_election_message_id VARCHAR(32), -- ID of the discord message used to display this user on the presidential election ballot.
PRIMARY KEY (commissar_id),
INDEX discord_index (discord_id)
);
Expand Down
18 changes: 18 additions & 0 deletions user-cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ async function LoadAllUsersFromDatabase() {
row.inactivity_tax_paid_until,
row.ban_conviction_time,
row.ban_pardon_time,
row.presidential_election_vote,
row.presidential_election_message_id,
);
newCache[row.commissar_id] = newUser;
});
Expand Down Expand Up @@ -112,6 +114,7 @@ async function CreateNewDatabaseUser(discordMember) {
null, null, null,
null, null, null,
0,
null, null, null, null, null,
);
commissarUserCache[commissar_id] = newUser;
return newUser;
Expand Down Expand Up @@ -242,6 +245,21 @@ function GetUsersWithRankAndScoreHigherThan(rankIndex, scoreThreshold) {
return matches;
}

function CountPresidentialElectionVotes() {
const votes = {};
for (const i in commissarUserCache) {
const u = commissarUserCache[i];
const v = u.presidential_election_vote;
if (v) {
if (!(v in votes)) {
votes[v] = 0;
}
votes[v]++;
}
}
return votes;
}

module.exports = {
BulkCentralityUpdate,
CountVoiceActiveUsers,
Expand Down
35 changes: 25 additions & 10 deletions yen.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ async function CalculateInactivityTaxForecast() {
// Users with no yen can't pay tax.
return;
}
if (user.yen <= 1) {
// Users with 1 or fewer yen are exempt from the tax.
if (user.yen <= 0) {
// Users with 0 yen cannot pay tax.
return;
}
if (!user.last_seen) {
Expand Down Expand Up @@ -96,8 +96,8 @@ async function CalculateInactivityTaxBase() {
// Users with no yen can't pay tax.
return;
}
if (user.yen <= 1) {
// Users with 1 or fewer yen are exempt from the tax.
if (user.yen <= 0) {
// Users with 0 yen cannot pay tax.
return;
}
if (!user.last_seen) {
Expand Down Expand Up @@ -223,9 +223,9 @@ async function UpdateTaxChannel() {
setTimeout(async () => {
await UpdateTaxChannel();
}, 60 * 1000);
// Lottery once an hour.
// Update tax channel once an hour.
setInterval(async () => {
await DoLottery();
await UpdateTaxChannel();
}, 3600 * 1000);

async function CalculateTaxPlan(yenToRaise) {
Expand Down Expand Up @@ -354,18 +354,28 @@ async function HandleTaxCommand(discordMessage) {
await UpdateTaxChannel();
}

// Delay the first lottery by 5 minutes after bot startup to
// avoid too frequent lotteries while testing bot code, which
// involves repeatedly restarting the bot.
let nextLotteryTime = Date.now() + (5 * 60 * 1000);

async function DoLottery() {
console.log('LOTTERY');
const currentTime = Date.now();
if (currentTime < nextLotteryTime) {
return;
}
const taxBase = await CalculateInactivityTaxBase();
let totalTaxBase = 0;
for (const i in taxBase) {
totalTaxBase += taxBase[i];
}
const minimumTaxBaseForLottery = 1010;
if (totalTaxBase < minimumTaxBaseForLottery) {
return;
}
console.log('totalTaxBase', totalTaxBase);
const maxPrizeYen = 10;
const targetPrize = Math.floor(0.1 * totalTaxBase);
console.log('targetPrize', targetPrize);
const prizeYen = Math.min(targetPrize, maxPrizeYen);
const prizeYen = 10;
const plan = await CalculateTaxPlan(prizeYen);
if (!plan) {
console.log('Could not raise enough tax revenue for lottery.');
Expand Down Expand Up @@ -400,6 +410,11 @@ async function DoLottery() {
await ImplementTaxPlan(plan, recipient);
await UpdateYenChannel();
await UpdateTaxChannel();
// Schedule the minimum time for the next lottery to stop it from
// happening too often.
const oneHour = 60 * 60 * 1000;
const randomJitter = (Math.random() * 2 - 1) * (15 * 60 * 1000);
nextLotteryTime = currentTime + oneHour + randomJitter;
}

async function UpdateYenChannel() {
Expand Down
Loading