Merge branch 'xp-system' into 1.4
This commit is contained in:
commit
306ff3a918
@ -17,6 +17,8 @@
|
|||||||
CurrencyDropAmountMax,
|
CurrencyDropAmountMax,
|
||||||
MinimumBetAmount,
|
MinimumBetAmount,
|
||||||
TriviaCurrencyReward,
|
TriviaCurrencyReward,
|
||||||
|
XpPerMessage,
|
||||||
|
XpMinutesTimeout,
|
||||||
|
|
||||||
//ErrorColor, //after i fix the nadekobot.cs static variables
|
//ErrorColor, //after i fix the nadekobot.cs static variables
|
||||||
//OkColor
|
//OkColor
|
||||||
|
1945
src/NadekoBot/Migrations/20170908230730_xp-and-clubs.Designer.cs
generated
Normal file
1945
src/NadekoBot/Migrations/20170908230730_xp-and-clubs.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
296
src/NadekoBot/Migrations/20170908230730_xp-and-clubs.cs
Normal file
296
src/NadekoBot/Migrations/20170908230730_xp-and-clubs.cs
Normal file
@ -0,0 +1,296 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations
|
||||||
|
{
|
||||||
|
public partial class xpandclubs : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "XpMinutesTimeout",
|
||||||
|
table: "BotConfig",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 5)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "XpPerMessage",
|
||||||
|
table: "BotConfig",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 3)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Clubs",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
DateAdded = table.Column<DateTime>(nullable: true),
|
||||||
|
Discrim = table.Column<int>(nullable: false),
|
||||||
|
ImageUrl = table.Column<string>(nullable: true),
|
||||||
|
MinimumLevelReq = table.Column<int>(nullable: false),
|
||||||
|
Name = table.Column<string>(maxLength: 20, nullable: false),
|
||||||
|
OwnerId = table.Column<int>(nullable: false),
|
||||||
|
Xp = table.Column<int>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Clubs", x => x.Id);
|
||||||
|
table.UniqueConstraint("AK_Clubs_Name_Discrim", x => new { x.Name, x.Discrim });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Clubs_DiscordUser_OwnerId",
|
||||||
|
column: x => x.OwnerId,
|
||||||
|
principalTable: "DiscordUser",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.Sql(MigrationQueries.UserClub);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "LastLevelUp",
|
||||||
|
table: "DiscordUser",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: DateTime.UtcNow);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "NotifyOnLevelUp",
|
||||||
|
table: "DiscordUser",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "UserXpStats",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
AwardedXp = table.Column<int>(nullable: false),
|
||||||
|
DateAdded = table.Column<DateTime>(nullable: true),
|
||||||
|
GuildId = table.Column<ulong>(nullable: false),
|
||||||
|
LastLevelUp = table.Column<DateTime>(nullable: false, defaultValue: new DateTime(2017, 9, 9, 1, 7, 29, 858, DateTimeKind.Local)),
|
||||||
|
NotifyOnLevelUp = table.Column<int>(nullable: false),
|
||||||
|
UserId = table.Column<ulong>(nullable: false),
|
||||||
|
Xp = table.Column<int>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_UserXpStats", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "XpSettings",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
DateAdded = table.Column<DateTime>(nullable: true),
|
||||||
|
GuildConfigId = table.Column<int>(nullable: false),
|
||||||
|
NotifyMessage = table.Column<string>(nullable: true),
|
||||||
|
ServerExcluded = table.Column<bool>(nullable: false),
|
||||||
|
XpRoleRewardExclusive = table.Column<bool>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_XpSettings", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_XpSettings_GuildConfigs_GuildConfigId",
|
||||||
|
column: x => x.GuildConfigId,
|
||||||
|
principalTable: "GuildConfigs",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ClubApplicants",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
ClubId = table.Column<int>(nullable: false),
|
||||||
|
UserId = table.Column<int>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ClubApplicants", x => new { x.ClubId, x.UserId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ClubApplicants_Clubs_ClubId",
|
||||||
|
column: x => x.ClubId,
|
||||||
|
principalTable: "Clubs",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ClubApplicants_DiscordUser_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "DiscordUser",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ClubBans",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
ClubId = table.Column<int>(nullable: false),
|
||||||
|
UserId = table.Column<int>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ClubBans", x => new { x.ClubId, x.UserId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ClubBans_Clubs_ClubId",
|
||||||
|
column: x => x.ClubId,
|
||||||
|
principalTable: "Clubs",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ClubBans_DiscordUser_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "DiscordUser",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ExcludedItem",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
DateAdded = table.Column<DateTime>(nullable: true),
|
||||||
|
ItemId = table.Column<ulong>(nullable: false),
|
||||||
|
ItemType = table.Column<int>(nullable: false),
|
||||||
|
XpSettingsId = table.Column<int>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ExcludedItem", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ExcludedItem_XpSettings_XpSettingsId",
|
||||||
|
column: x => x.XpSettingsId,
|
||||||
|
principalTable: "XpSettings",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "XpRoleReward",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
DateAdded = table.Column<DateTime>(nullable: true),
|
||||||
|
Level = table.Column<int>(nullable: false),
|
||||||
|
RoleId = table.Column<ulong>(nullable: false),
|
||||||
|
XpSettingsId = table.Column<int>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_XpRoleReward", x => x.Id);
|
||||||
|
table.UniqueConstraint("AK_XpRoleReward_Level", x => x.Level);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_XpRoleReward_XpSettings_XpSettingsId",
|
||||||
|
column: x => x.XpSettingsId,
|
||||||
|
principalTable: "XpSettings",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_DiscordUser_ClubId",
|
||||||
|
table: "DiscordUser",
|
||||||
|
column: "ClubId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ClubApplicants_UserId",
|
||||||
|
table: "ClubApplicants",
|
||||||
|
column: "UserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ClubBans_UserId",
|
||||||
|
table: "ClubBans",
|
||||||
|
column: "UserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Clubs_OwnerId",
|
||||||
|
table: "Clubs",
|
||||||
|
column: "OwnerId",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ExcludedItem_XpSettingsId",
|
||||||
|
table: "ExcludedItem",
|
||||||
|
column: "XpSettingsId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_UserXpStats_UserId_GuildId",
|
||||||
|
table: "UserXpStats",
|
||||||
|
columns: new[] { "UserId", "GuildId" },
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_XpRoleReward_XpSettingsId",
|
||||||
|
table: "XpRoleReward",
|
||||||
|
column: "XpSettingsId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_XpSettings_GuildConfigId",
|
||||||
|
table: "XpSettings",
|
||||||
|
column: "GuildConfigId",
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_DiscordUser_Clubs_ClubId",
|
||||||
|
table: "DiscordUser");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ClubApplicants");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ClubBans");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ExcludedItem");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "UserXpStats");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "XpRoleReward");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Clubs");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "XpSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_DiscordUser_ClubId",
|
||||||
|
table: "DiscordUser");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ClubId",
|
||||||
|
table: "DiscordUser");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "LastLevelUp",
|
||||||
|
table: "DiscordUser");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "NotifyOnLevelUp",
|
||||||
|
table: "DiscordUser");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "XpMinutesTimeout",
|
||||||
|
table: "BotConfig");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "XpPerMessage",
|
||||||
|
table: "BotConfig");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
src/NadekoBot/Migrations/MigrationQueries.cs
Normal file
38
src/NadekoBot/Migrations/MigrationQueries.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
namespace NadekoBot.Migrations
|
||||||
|
{
|
||||||
|
internal class MigrationQueries
|
||||||
|
{
|
||||||
|
public static string UserClub { get; } = @"
|
||||||
|
CREATE TABLE DiscordUser_tmp(
|
||||||
|
Id INTEGER PRIMARY KEY,
|
||||||
|
AvatarId TEXT,
|
||||||
|
Discriminator TEXT,
|
||||||
|
UserId INTEGER UNIQUE NOT NULL,
|
||||||
|
DateAdded TEXT,
|
||||||
|
Username TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO DiscordUser_tmp
|
||||||
|
SELECT Id, AvatarId, Discriminator, UserId, DateAdded, Username
|
||||||
|
FROM DiscordUser;
|
||||||
|
|
||||||
|
DROP TABLE DiscordUser;
|
||||||
|
|
||||||
|
CREATE TABLE DiscordUser(
|
||||||
|
Id INTEGER PRIMARY KEY,
|
||||||
|
AvatarId TEXT,
|
||||||
|
Discriminator TEXT,
|
||||||
|
UserId INTEGER UNIQUE NOT NULL,
|
||||||
|
DateAdded TEXT,
|
||||||
|
Username TEXT,
|
||||||
|
ClubId INTEGER,
|
||||||
|
CONSTRAINT FK_DiscordUser_Clubs_ClubId FOREIGN KEY(ClubId) REFERENCES Clubs(Id) ON DELETE RESTRICT
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO DiscordUser
|
||||||
|
SELECT Id, AvatarId, Discriminator, UserId, DateAdded, Username, NULL
|
||||||
|
FROM DiscordUser_tmp;
|
||||||
|
|
||||||
|
DROP TABLE DiscordUser_tmp;";
|
||||||
|
}
|
||||||
|
}
|
@ -183,6 +183,14 @@ namespace NadekoBot.Migrations
|
|||||||
|
|
||||||
b.Property<int>("TriviaCurrencyReward");
|
b.Property<int>("TriviaCurrencyReward");
|
||||||
|
|
||||||
|
b.Property<int>("XpMinutesTimeout")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasDefaultValue(5);
|
||||||
|
|
||||||
|
b.Property<int>("XpPerMessage")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasDefaultValue(3);
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.ToTable("BotConfig");
|
b.ToTable("BotConfig");
|
||||||
@ -238,6 +246,63 @@ namespace NadekoBot.Migrations
|
|||||||
b.ToTable("ClashOfClans");
|
b.ToTable("ClashOfClans");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClubApplicants", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("ClubId");
|
||||||
|
|
||||||
|
b.Property<int>("UserId");
|
||||||
|
|
||||||
|
b.HasKey("ClubId", "UserId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("ClubApplicants");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClubBans", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("ClubId");
|
||||||
|
|
||||||
|
b.Property<int>("UserId");
|
||||||
|
|
||||||
|
b.HasKey("ClubId", "UserId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("ClubBans");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClubInfo", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateAdded");
|
||||||
|
|
||||||
|
b.Property<int>("Discrim");
|
||||||
|
|
||||||
|
b.Property<string>("ImageUrl");
|
||||||
|
|
||||||
|
b.Property<int>("MinimumLevelReq");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20);
|
||||||
|
|
||||||
|
b.Property<int>("OwnerId");
|
||||||
|
|
||||||
|
b.Property<int>("Xp");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasAlternateKey("Name", "Discrim");
|
||||||
|
|
||||||
|
b.HasIndex("OwnerId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Clubs");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandAlias", b =>
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandAlias", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@ -391,10 +456,18 @@ namespace NadekoBot.Migrations
|
|||||||
|
|
||||||
b.Property<string>("AvatarId");
|
b.Property<string>("AvatarId");
|
||||||
|
|
||||||
|
b.Property<int?>("ClubId");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded");
|
b.Property<DateTime?>("DateAdded");
|
||||||
|
|
||||||
b.Property<string>("Discriminator");
|
b.Property<string>("Discriminator");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastLevelUp")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasDefaultValue(new DateTime(2017, 9, 9, 1, 7, 29, 857, DateTimeKind.Local));
|
||||||
|
|
||||||
|
b.Property<int>("NotifyOnLevelUp");
|
||||||
|
|
||||||
b.Property<ulong>("UserId");
|
b.Property<ulong>("UserId");
|
||||||
|
|
||||||
b.Property<string>("Username");
|
b.Property<string>("Username");
|
||||||
@ -403,6 +476,8 @@ namespace NadekoBot.Migrations
|
|||||||
|
|
||||||
b.HasAlternateKey("UserId");
|
b.HasAlternateKey("UserId");
|
||||||
|
|
||||||
|
b.HasIndex("ClubId");
|
||||||
|
|
||||||
b.ToTable("DiscordUser");
|
b.ToTable("DiscordUser");
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -445,6 +520,26 @@ namespace NadekoBot.Migrations
|
|||||||
b.ToTable("EightBallResponses");
|
b.ToTable("EightBallResponses");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.ExcludedItem", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateAdded");
|
||||||
|
|
||||||
|
b.Property<ulong>("ItemId");
|
||||||
|
|
||||||
|
b.Property<int>("ItemType");
|
||||||
|
|
||||||
|
b.Property<int?>("XpSettingsId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("XpSettingsId");
|
||||||
|
|
||||||
|
b.ToTable("ExcludedItem");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b =>
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@ -1252,6 +1347,35 @@ namespace NadekoBot.Migrations
|
|||||||
b.ToTable("PokeGame");
|
b.ToTable("PokeGame");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.UserXpStats", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("AwardedXp");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateAdded");
|
||||||
|
|
||||||
|
b.Property<ulong>("GuildId");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastLevelUp")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasDefaultValue(new DateTime(2017, 9, 9, 1, 7, 29, 858, DateTimeKind.Local));
|
||||||
|
|
||||||
|
b.Property<int>("NotifyOnLevelUp");
|
||||||
|
|
||||||
|
b.Property<ulong>("UserId");
|
||||||
|
|
||||||
|
b.Property<int>("Xp");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId", "GuildId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("UserXpStats");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.VcRoleInfo", b =>
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.VcRoleInfo", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@ -1393,6 +1517,51 @@ namespace NadekoBot.Migrations
|
|||||||
b.ToTable("WarningPunishment");
|
b.ToTable("WarningPunishment");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.XpRoleReward", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateAdded");
|
||||||
|
|
||||||
|
b.Property<int>("Level");
|
||||||
|
|
||||||
|
b.Property<ulong>("RoleId");
|
||||||
|
|
||||||
|
b.Property<int?>("XpSettingsId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasAlternateKey("Level");
|
||||||
|
|
||||||
|
b.HasIndex("XpSettingsId");
|
||||||
|
|
||||||
|
b.ToTable("XpRoleReward");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.XpSettings", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateAdded");
|
||||||
|
|
||||||
|
b.Property<int>("GuildConfigId");
|
||||||
|
|
||||||
|
b.Property<string>("NotifyMessage");
|
||||||
|
|
||||||
|
b.Property<bool>("ServerExcluded");
|
||||||
|
|
||||||
|
b.Property<bool>("XpRoleRewardExclusive");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GuildConfigId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("XpSettings");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b =>
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig")
|
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig")
|
||||||
@ -1442,6 +1611,40 @@ namespace NadekoBot.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClubApplicants", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.ClubInfo", "Club")
|
||||||
|
.WithMany("Applicants")
|
||||||
|
.HasForeignKey("ClubId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClubBans", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.ClubInfo", "Club")
|
||||||
|
.WithMany("Bans")
|
||||||
|
.HasForeignKey("ClubId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClubInfo", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "Owner")
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey("NadekoBot.Services.Database.Models.ClubInfo", "OwnerId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandAlias", b =>
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandAlias", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
|
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
|
||||||
@ -1463,6 +1666,13 @@ namespace NadekoBot.Migrations
|
|||||||
.HasForeignKey("BotConfigId");
|
.HasForeignKey("BotConfigId");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.DiscordUser", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.ClubInfo", "Club")
|
||||||
|
.WithMany("Users")
|
||||||
|
.HasForeignKey("ClubId");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b =>
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
|
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
|
||||||
@ -1470,6 +1680,13 @@ namespace NadekoBot.Migrations
|
|||||||
.HasForeignKey("BotConfigId");
|
.HasForeignKey("BotConfigId");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.ExcludedItem", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.XpSettings")
|
||||||
|
.WithMany("ExclusionList")
|
||||||
|
.HasForeignKey("XpSettingsId");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b =>
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
|
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
|
||||||
@ -1707,6 +1924,21 @@ namespace NadekoBot.Migrations
|
|||||||
.WithMany("WarnPunishments")
|
.WithMany("WarnPunishments")
|
||||||
.HasForeignKey("GuildConfigId");
|
.HasForeignKey("GuildConfigId");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.XpRoleReward", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.XpSettings")
|
||||||
|
.WithMany("RoleRewards")
|
||||||
|
.HasForeignKey("XpSettingsId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.XpSettings", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig")
|
||||||
|
.WithOne("XpSettings")
|
||||||
|
.HasForeignKey("NadekoBot.Services.Database.Models.XpSettings", "GuildConfigId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -352,54 +352,6 @@ namespace NadekoBot.Modules.Administration
|
|||||||
oldConfig.RotatingStatuses.ForEach(i => messages.Add(new PlayingStatus { Status = i }));
|
oldConfig.RotatingStatuses.ForEach(i => messages.Add(new PlayingStatus { Status = i }));
|
||||||
botConfig.RotatingStatusMessages = messages;
|
botConfig.RotatingStatusMessages = messages;
|
||||||
|
|
||||||
//Prefix
|
|
||||||
botConfig.ModulePrefixes.Clear();
|
|
||||||
botConfig.ModulePrefixes.AddRange(new HashSet<ModulePrefix>
|
|
||||||
{
|
|
||||||
new ModulePrefix()
|
|
||||||
{
|
|
||||||
ModuleName = "Administration",
|
|
||||||
Prefix = oldConfig.CommandPrefixes.Administration
|
|
||||||
},
|
|
||||||
new ModulePrefix()
|
|
||||||
{
|
|
||||||
ModuleName = "Searches",
|
|
||||||
Prefix = oldConfig.CommandPrefixes.Searches
|
|
||||||
},
|
|
||||||
new ModulePrefix() {ModuleName = "NSFW", Prefix = oldConfig.CommandPrefixes.NSFW},
|
|
||||||
new ModulePrefix()
|
|
||||||
{
|
|
||||||
ModuleName = "Conversations",
|
|
||||||
Prefix = oldConfig.CommandPrefixes.Conversations
|
|
||||||
},
|
|
||||||
new ModulePrefix()
|
|
||||||
{
|
|
||||||
ModuleName = "ClashOfClans",
|
|
||||||
Prefix = oldConfig.CommandPrefixes.ClashOfClans
|
|
||||||
},
|
|
||||||
new ModulePrefix() {ModuleName = "Help", Prefix = oldConfig.CommandPrefixes.Help},
|
|
||||||
new ModulePrefix() {ModuleName = "Music", Prefix = oldConfig.CommandPrefixes.Music},
|
|
||||||
new ModulePrefix() {ModuleName = "Trello", Prefix = oldConfig.CommandPrefixes.Trello},
|
|
||||||
new ModulePrefix() {ModuleName = "Games", Prefix = oldConfig.CommandPrefixes.Games},
|
|
||||||
new ModulePrefix()
|
|
||||||
{
|
|
||||||
ModuleName = "Gambling",
|
|
||||||
Prefix = oldConfig.CommandPrefixes.Gambling
|
|
||||||
},
|
|
||||||
new ModulePrefix()
|
|
||||||
{
|
|
||||||
ModuleName = "Permissions",
|
|
||||||
Prefix = oldConfig.CommandPrefixes.Permissions
|
|
||||||
},
|
|
||||||
new ModulePrefix()
|
|
||||||
{
|
|
||||||
ModuleName = "Programming",
|
|
||||||
Prefix = oldConfig.CommandPrefixes.Programming
|
|
||||||
},
|
|
||||||
new ModulePrefix() {ModuleName = "Pokemon", Prefix = oldConfig.CommandPrefixes.Pokemon},
|
|
||||||
new ModulePrefix() {ModuleName = "Utility", Prefix = oldConfig.CommandPrefixes.Utility}
|
|
||||||
});
|
|
||||||
|
|
||||||
//Blacklist
|
//Blacklist
|
||||||
var blacklist = new HashSet<BlacklistItem>(oldConfig.ServerBlacklist.Select(server => new BlacklistItem() { ItemId = server, Type = BlacklistType.Server }));
|
var blacklist = new HashSet<BlacklistItem>(oldConfig.ServerBlacklist.Select(server => new BlacklistItem() { ItemId = server, Type = BlacklistType.Server }));
|
||||||
blacklist.AddRange(oldConfig.ChannelBlacklist.Select(channel => new BlacklistItem() { ItemId = channel, Type = BlacklistType.Channel }));
|
blacklist.AddRange(oldConfig.ChannelBlacklist.Select(channel => new BlacklistItem() { ItemId = channel, Type = BlacklistType.Channel }));
|
||||||
|
@ -153,7 +153,7 @@ namespace NadekoBot.Modules.CustomReactions
|
|||||||
if (Context.Guild == null) // its a private one, just send back
|
if (Context.Guild == null) // its a private one, just send back
|
||||||
await Context.Channel.SendFileAsync(txtStream, "customreactions.txt", GetText("list_all")).ConfigureAwait(false);
|
await Context.Channel.SendFileAsync(txtStream, "customreactions.txt", GetText("list_all")).ConfigureAwait(false);
|
||||||
else
|
else
|
||||||
await ((IGuildUser)Context.User).SendFileAsync(txtStream, "customreactions.txt", GetText("list_all")).ConfigureAwait(false);
|
await ((IGuildUser)Context.User).SendFileAsync(txtStream, "customreactions.txt", GetText("list_all"), false).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
@ -48,8 +48,8 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
[Group]
|
[Group]
|
||||||
public class WaifuClaimCommands : NadekoSubmodule
|
public class WaifuClaimCommands : NadekoSubmodule
|
||||||
{
|
{
|
||||||
private static ConcurrentDictionary<ulong, DateTime> divorceCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>();
|
private static ConcurrentDictionary<ulong, DateTime> _divorceCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>();
|
||||||
private static ConcurrentDictionary<ulong, DateTime> affinityCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>();
|
private static ConcurrentDictionary<ulong, DateTime> _affinityCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>();
|
||||||
|
|
||||||
enum WaifuClaimResult
|
enum WaifuClaimResult
|
||||||
{
|
{
|
||||||
@ -219,7 +219,7 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
var now = DateTime.UtcNow;
|
var now = DateTime.UtcNow;
|
||||||
if (w?.Claimer == null || w.Claimer.UserId != Context.User.Id)
|
if (w?.Claimer == null || w.Claimer.UserId != Context.User.Id)
|
||||||
result = DivorceResult.NotYourWife;
|
result = DivorceResult.NotYourWife;
|
||||||
else if (divorceCooldowns.AddOrUpdate(Context.User.Id,
|
else if (_divorceCooldowns.AddOrUpdate(Context.User.Id,
|
||||||
now,
|
now,
|
||||||
(key, old) => ((difference = now.Subtract(old)) > _divorceLimit) ? now : old) != now)
|
(key, old) => ((difference = now.Subtract(old)) > _divorceLimit) ? now : old) != now)
|
||||||
{
|
{
|
||||||
@ -303,7 +303,7 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
if (w?.Affinity?.UserId == u?.Id)
|
if (w?.Affinity?.UserId == u?.Id)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
else if (affinityCooldowns.AddOrUpdate(Context.User.Id,
|
else if (_affinityCooldowns.AddOrUpdate(Context.User.Id,
|
||||||
now,
|
now,
|
||||||
(key, old) => ((difference = now.Subtract(old)) > _affinityLimit) ? now : old) != now)
|
(key, old) => ((difference = now.Subtract(old)) > _affinityLimit) ? now : old) != now)
|
||||||
{
|
{
|
||||||
@ -459,7 +459,7 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
.AddField(efb => efb.WithName(GetText("likes")).WithValue(w.Affinity?.ToString() ?? nobody).WithIsInline(true))
|
.AddField(efb => efb.WithName(GetText("likes")).WithValue(w.Affinity?.ToString() ?? nobody).WithIsInline(true))
|
||||||
.AddField(efb => efb.WithName(GetText("changes_of_heart")).WithValue($"{affInfo.Count} - \"the {affInfo.Title}\"").WithIsInline(true))
|
.AddField(efb => efb.WithName(GetText("changes_of_heart")).WithValue($"{affInfo.Count} - \"the {affInfo.Title}\"").WithIsInline(true))
|
||||||
.AddField(efb => efb.WithName(GetText("divorces")).WithValue(divorces.ToString()).WithIsInline(true))
|
.AddField(efb => efb.WithName(GetText("divorces")).WithValue(divorces.ToString()).WithIsInline(true))
|
||||||
.AddField(efb => efb.WithName(GetText("gifts")).WithValue(!w.Items.Any() ? "-" : string.Join("\n", w.Items.OrderBy(x => x.Price).GroupBy(x => x.ItemEmoji).Select(x => $"{x.Key} x{x.Count()}"))).WithIsInline(true))
|
.AddField(efb => efb.WithName(GetText("gifts")).WithValue(!w.Items.Any() ? "-" : string.Join("\n", w.Items.OrderBy(x => x.Price).GroupBy(x => x.ItemEmoji).Select(x => $"{x.Key} x{x.Count()}"))).WithIsInline(false))
|
||||||
.AddField(efb => efb.WithName($"Waifus ({claims.Count})").WithValue(claims.Count == 0 ? nobody : string.Join("\n", claims.OrderBy(x => rng.Next()).Take(30).Select(x => x.Waifu))).WithIsInline(false));
|
.AddField(efb => efb.WithName($"Waifus ({claims.Count})").WithValue(claims.Count == 0 ? nobody : string.Join("\n", claims.OrderBy(x => rng.Next()).Take(30).Select(x => x.Waifu))).WithIsInline(false));
|
||||||
|
|
||||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||||
|
@ -11,7 +11,7 @@ using NadekoBot.Modules.Games.Services;
|
|||||||
|
|
||||||
namespace NadekoBot.Modules.Games
|
namespace NadekoBot.Modules.Games
|
||||||
{
|
{
|
||||||
/*todo more games
|
/* more games
|
||||||
- Blackjack
|
- Blackjack
|
||||||
- Shiritori
|
- Shiritori
|
||||||
- Simple RPG adventure
|
- Simple RPG adventure
|
||||||
|
@ -28,7 +28,7 @@ namespace NadekoBot.Modules.Games.Services
|
|||||||
public readonly ImmutableArray<string> EightBallResponses;
|
public readonly ImmutableArray<string> EightBallResponses;
|
||||||
|
|
||||||
private readonly Timer _t;
|
private readonly Timer _t;
|
||||||
private readonly DiscordSocketClient _client;
|
private readonly CommandHandler _cmd;
|
||||||
private readonly NadekoStrings _strings;
|
private readonly NadekoStrings _strings;
|
||||||
private readonly IImagesService _images;
|
private readonly IImagesService _images;
|
||||||
private readonly Logger _log;
|
private readonly Logger _log;
|
||||||
@ -38,11 +38,11 @@ namespace NadekoBot.Modules.Games.Services
|
|||||||
|
|
||||||
public List<TypingArticle> TypingArticles { get; } = new List<TypingArticle>();
|
public List<TypingArticle> TypingArticles { get; } = new List<TypingArticle>();
|
||||||
|
|
||||||
public GamesService(DiscordSocketClient client, IBotConfigProvider bc, IEnumerable<GuildConfig> gcs,
|
public GamesService(CommandHandler cmd, IBotConfigProvider bc, IEnumerable<GuildConfig> gcs,
|
||||||
NadekoStrings strings, IImagesService images, CommandHandler cmdHandler)
|
NadekoStrings strings, IImagesService images, CommandHandler cmdHandler)
|
||||||
{
|
{
|
||||||
_bc = bc;
|
_bc = bc;
|
||||||
_client = client;
|
_cmd = cmd;
|
||||||
_strings = strings;
|
_strings = strings;
|
||||||
_images = images;
|
_images = images;
|
||||||
_cmdHandler = cmdHandler;
|
_cmdHandler = cmdHandler;
|
||||||
@ -59,7 +59,7 @@ namespace NadekoBot.Modules.Games.Services
|
|||||||
}, null, TimeSpan.FromDays(1), TimeSpan.FromDays(1));
|
}, null, TimeSpan.FromDays(1), TimeSpan.FromDays(1));
|
||||||
|
|
||||||
//plantpick
|
//plantpick
|
||||||
client.MessageReceived += PotentialFlowerGeneration;
|
_cmd.OnMessageNoTrigger += PotentialFlowerGeneration;
|
||||||
GenerationChannels = new ConcurrentHashSet<ulong>(gcs
|
GenerationChannels = new ConcurrentHashSet<ulong>(gcs
|
||||||
.SelectMany(c => c.GenerateCurrencyChannelIds.Select(obj => obj.ChannelId)));
|
.SelectMany(c => c.GenerateCurrencyChannelIds.Select(obj => obj.ChannelId)));
|
||||||
|
|
||||||
@ -102,7 +102,7 @@ namespace NadekoBot.Modules.Games.Services
|
|||||||
private string GetText(ITextChannel ch, string key, params object[] rep)
|
private string GetText(ITextChannel ch, string key, params object[] rep)
|
||||||
=> _strings.GetText(key, ch.GuildId, "Games".ToLowerInvariant(), rep);
|
=> _strings.GetText(key, ch.GuildId, "Games".ToLowerInvariant(), rep);
|
||||||
|
|
||||||
private Task PotentialFlowerGeneration(SocketMessage imsg)
|
private Task PotentialFlowerGeneration(IUserMessage imsg)
|
||||||
{
|
{
|
||||||
var msg = imsg as SocketUserMessage;
|
var msg = imsg as SocketUserMessage;
|
||||||
if (msg == null || msg.Author.IsBot)
|
if (msg == null || msg.Author.IsBot)
|
||||||
|
@ -15,6 +15,7 @@ using NadekoBot.Modules.Searches.Common;
|
|||||||
using NadekoBot.Modules.Searches.Services;
|
using NadekoBot.Modules.Searches.Services;
|
||||||
using NadekoBot.Modules.NSFW.Exceptions;
|
using NadekoBot.Modules.NSFW.Exceptions;
|
||||||
|
|
||||||
|
//todo static httpclient
|
||||||
namespace NadekoBot.Modules.NSFW
|
namespace NadekoBot.Modules.NSFW
|
||||||
{
|
{
|
||||||
public class NSFW : NadekoTopLevelModule<SearchesService>
|
public class NSFW : NadekoTopLevelModule<SearchesService>
|
||||||
|
@ -10,7 +10,6 @@ using System.Threading.Tasks;
|
|||||||
using NadekoBot.Common;
|
using NadekoBot.Common;
|
||||||
using NadekoBot.Common.Attributes;
|
using NadekoBot.Common.Attributes;
|
||||||
|
|
||||||
//todo 50 drawing
|
|
||||||
namespace NadekoBot.Modules.Searches
|
namespace NadekoBot.Modules.Searches
|
||||||
{
|
{
|
||||||
public partial class Searches
|
public partial class Searches
|
||||||
@ -66,320 +65,4 @@ namespace NadekoBot.Modules.Searches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// private class CachedChampion
|
|
||||||
// {
|
|
||||||
// public System.IO.Stream ImageStream { get; set; }
|
|
||||||
// public DateTime AddedAt { get; set; }
|
|
||||||
// public string Name { get; set; }
|
|
||||||
// }
|
|
||||||
|
|
||||||
//
|
|
||||||
// private static Dictionary<string, CachedChampion> CachedChampionImages = new Dictionary<string, CachedChampion>();
|
|
||||||
|
|
||||||
// private System.Timers.Timer clearTimer { get; } = new System.Timers.Timer();
|
|
||||||
// public LoLCommands(DiscordModule module) : base(module)
|
|
||||||
// {
|
|
||||||
// clearTimer.Interval = new TimeSpan(0, 10, 0).TotalMilliseconds;
|
|
||||||
// clearTimer.Start();
|
|
||||||
// clearTimer.Elapsed += (s, e) =>
|
|
||||||
// {
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// CachedChampionImages = CachedChampionImages
|
|
||||||
// .Where(kvp => DateTime.UtcNow - kvp.Value.AddedAt > new TimeSpan(1, 0, 0))
|
|
||||||
// .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
|
||||||
// }
|
|
||||||
// catch { }
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public Func<CommandEventArgs, Task> DoFunc()
|
|
||||||
// {
|
|
||||||
// throw new NotImplementedException();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private class MatchupModel
|
|
||||||
// {
|
|
||||||
// public int Games { get; set; }
|
|
||||||
// public float WinRate { get; set; }
|
|
||||||
// [Newtonsoft.Json.JsonProperty("key")]
|
|
||||||
// public string Name { get; set; }
|
|
||||||
// public float StatScore { get; set; }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public override void Init(CommandGroupBuilder cgb)
|
|
||||||
// {
|
|
||||||
// cgb.CreateCommand(Module.Name + "lolchamp")
|
|
||||||
// .Description($"Shows League Of Legends champion statistics. If there are spaces/apostrophes or in the name - omit them. Optional second parameter is a role. |`{Prefix}lolchamp Riven` or `{Prefix}lolchamp Annie sup`")
|
|
||||||
// .Parameter("champ", ParameterType.Required)
|
|
||||||
// .Parameter("position", ParameterType.Unparsed)
|
|
||||||
// .Do(async e =>
|
|
||||||
// {
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// //get role
|
|
||||||
// var role = ResolvePos(position);
|
|
||||||
// var resolvedRole = role;
|
|
||||||
// var name = champ.Replace(" ", "").ToLower();
|
|
||||||
// CachedChampion champ = null;
|
|
||||||
|
|
||||||
// if (CachedChampionImages.TryGetValue(name + "_" + resolvedRole, out champ))
|
|
||||||
// if (champ != null)
|
|
||||||
// {
|
|
||||||
// champ.ImageStream.Position = 0;
|
|
||||||
// await e.Channel.SendFile("champ.png", champ.ImageStream).ConfigureAwait(false);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// var allData = JArray.Parse(await Classes.http.GetStringAsync($"http://api.champion.gg/champion/{name}?api_key={_creds.LOLAPIKey}").ConfigureAwait(false));
|
|
||||||
// JToken data = null;
|
|
||||||
// if (role != null)
|
|
||||||
// {
|
|
||||||
// for (var i = 0; i < allData.Count; i++)
|
|
||||||
// {
|
|
||||||
// if (allData[i]["role"].ToString().Equals(role))
|
|
||||||
// {
|
|
||||||
// data = allData[i];
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if (data == null)
|
|
||||||
// {
|
|
||||||
// await channel.SendMessageAsync("💢 Data for that role does not exist.").ConfigureAwait(false);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// data = allData[0];
|
|
||||||
// role = allData[0]["role"].ToString();
|
|
||||||
// resolvedRole = ResolvePos(role);
|
|
||||||
// }
|
|
||||||
// if (CachedChampionImages.TryGetValue(name + "_" + resolvedRole, out champ))
|
|
||||||
// if (champ != null)
|
|
||||||
// {
|
|
||||||
// champ.ImageStream.Position = 0;
|
|
||||||
// await e.Channel.SendFile("champ.png", champ.ImageStream).ConfigureAwait(false);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// //name = data["title"].ToString();
|
|
||||||
// // get all possible roles, and "select" the shown one
|
|
||||||
// var roles = new string[allData.Count];
|
|
||||||
// for (var i = 0; i < allData.Count; i++)
|
|
||||||
// {
|
|
||||||
// roles[i] = allData[i]["role"].ToString();
|
|
||||||
// if (roles[i] == role)
|
|
||||||
// roles[i] = ">" + roles[i] + "<";
|
|
||||||
// }
|
|
||||||
// var general = JArray.Parse(await http.GetStringAsync($"http://api.champion.gg/stats/" +
|
|
||||||
// $"champs/{name}?api_key={_creds.LOLAPIKey}")
|
|
||||||
// .ConfigureAwait(false))
|
|
||||||
// .FirstOrDefault(jt => jt["role"].ToString() == role)?["general"];
|
|
||||||
// if (general == null)
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// //get build data for this role
|
|
||||||
// var buildData = data["items"]["mostGames"]["items"];
|
|
||||||
// var items = new string[6];
|
|
||||||
// for (var i = 0; i < 6; i++)
|
|
||||||
// {
|
|
||||||
// items[i] = buildData[i]["id"].ToString();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// //get matchup data to show counters and countered champions
|
|
||||||
// var matchupDataIE = data["matchups"].ToObject<List<MatchupModel>>();
|
|
||||||
|
|
||||||
// var matchupData = matchupDataIE.OrderBy(m => m.StatScore).ToArray();
|
|
||||||
|
|
||||||
// var countered = new[] { matchupData[0].Name, matchupData[1].Name, matchupData[2].Name };
|
|
||||||
// var counters = new[] { matchupData[matchupData.Length - 1].Name, matchupData[matchupData.Length - 2].Name, matchupData[matchupData.Length - 3].Name };
|
|
||||||
|
|
||||||
// //get runes data
|
|
||||||
// var runesJArray = data["runes"]["mostGames"]["runes"] as JArray;
|
|
||||||
// var runes = string.Join("\n", runesJArray.OrderBy(jt => int.Parse(jt["number"].ToString())).Select(jt => jt["number"].ToString() + "x" + jt["name"]));
|
|
||||||
|
|
||||||
// // get masteries data
|
|
||||||
|
|
||||||
// var masteries = (data["masteries"]["mostGames"]["masteries"] as JArray);
|
|
||||||
|
|
||||||
// //get skill order data<API_KEY>
|
|
||||||
|
|
||||||
// var orderArr = (data["skills"]["mostGames"]["order"] as JArray);
|
|
||||||
|
|
||||||
// var img = Image.FromFile("data/lol/bg.png");
|
|
||||||
// using (var g = Graphics.FromImage(img))
|
|
||||||
// {
|
|
||||||
// g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
|
|
||||||
// //g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
|
|
||||||
// const int margin = 5;
|
|
||||||
// const int imageSize = 75;
|
|
||||||
// var normalFont = new Font("Monaco", 8, FontStyle.Regular);
|
|
||||||
// var smallFont = new Font("Monaco", 7, FontStyle.Regular);
|
|
||||||
// //draw champ image
|
|
||||||
// var champName = data["key"].ToString().Replace(" ", "");
|
|
||||||
|
|
||||||
// g.DrawImage(GetImage(champName), new Rectangle(margin, margin, imageSize, imageSize));
|
|
||||||
// //draw champ name
|
|
||||||
// if (champName == "MonkeyKing")
|
|
||||||
// champName = "Wukong";
|
|
||||||
// g.DrawString($"{champName}", new Font("Times New Roman", 24, FontStyle.Regular), Brushes.WhiteSmoke, margin + imageSize + margin, margin);
|
|
||||||
// //draw champ surname
|
|
||||||
|
|
||||||
// //draw skill order
|
|
||||||
// if (orderArr.Count != 0)
|
|
||||||
// {
|
|
||||||
// float orderFormula = 120 / orderArr.Count;
|
|
||||||
// const float orderVerticalSpacing = 10;
|
|
||||||
// for (var i = 0; i < orderArr.Count; i++)
|
|
||||||
// {
|
|
||||||
// var orderX = margin + margin + imageSize + orderFormula * i + i;
|
|
||||||
// float orderY = margin + 35;
|
|
||||||
// var spellName = orderArr[i].ToString().ToLowerInvariant();
|
|
||||||
|
|
||||||
// switch (spellName)
|
|
||||||
// {
|
|
||||||
// case "w":
|
|
||||||
// orderY += orderVerticalSpacing;
|
|
||||||
// break;
|
|
||||||
// case "e":
|
|
||||||
// orderY += orderVerticalSpacing * 2;
|
|
||||||
// break;
|
|
||||||
// case "r":
|
|
||||||
// orderY += orderVerticalSpacing * 3;
|
|
||||||
// break;
|
|
||||||
// default:
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// g.DrawString(spellName.ToUpperInvariant(), new Font("Monaco", 7), Brushes.LimeGreen, orderX, orderY);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// //draw roles
|
|
||||||
// g.DrawString("Roles: " + string.Join(", ", roles), normalFont, Brushes.WhiteSmoke, margin, margin + imageSize + margin);
|
|
||||||
|
|
||||||
// //draw average stats
|
|
||||||
// g.DrawString(
|
|
||||||
//$@" Average Stats
|
|
||||||
|
|
||||||
//Kills: {general["kills"]} CS: {general["minionsKilled"]}
|
|
||||||
//Deaths: {general["deaths"]} Win: {general["winPercent"]}%
|
|
||||||
//Assists: {general["assists"]} Ban: {general["banRate"]}%
|
|
||||||
//", normalFont, Brushes.WhiteSmoke, img.Width - 150, margin);
|
|
||||||
// //draw masteries
|
|
||||||
// g.DrawString($"Masteries: {string.Join(" / ", masteries?.Select(jt => jt["total"]))}", normalFont, Brushes.WhiteSmoke, margin, margin + imageSize + margin + 20);
|
|
||||||
// //draw runes
|
|
||||||
// g.DrawString($"{runes}", smallFont, Brushes.WhiteSmoke, margin, margin + imageSize + margin + 40);
|
|
||||||
// //draw counters
|
|
||||||
// g.DrawString($"Best against", smallFont, Brushes.WhiteSmoke, margin, img.Height - imageSize + margin);
|
|
||||||
// var smallImgSize = 50;
|
|
||||||
|
|
||||||
// for (var i = 0; i < counters.Length; i++)
|
|
||||||
// {
|
|
||||||
// g.DrawImage(GetImage(counters[i]),
|
|
||||||
// new Rectangle(i * (smallImgSize + margin) + margin, img.Height - smallImgSize - margin,
|
|
||||||
// smallImgSize,
|
|
||||||
// smallImgSize));
|
|
||||||
// }
|
|
||||||
// //draw countered by
|
|
||||||
// g.DrawString($"Worst against", smallFont, Brushes.WhiteSmoke, img.Width - 3 * (smallImgSize + margin), img.Height - imageSize + margin);
|
|
||||||
|
|
||||||
// for (var i = 0; i < countered.Length; i++)
|
|
||||||
// {
|
|
||||||
// var j = countered.Length - i;
|
|
||||||
// g.DrawImage(GetImage(countered[i]),
|
|
||||||
// new Rectangle(img.Width - (j * (smallImgSize + margin) + margin), img.Height - smallImgSize - margin,
|
|
||||||
// smallImgSize,
|
|
||||||
// smallImgSize));
|
|
||||||
// }
|
|
||||||
// //draw item build
|
|
||||||
// g.DrawString("Popular build", normalFont, Brushes.WhiteSmoke, img.Width - (3 * (smallImgSize + margin) + margin), 77);
|
|
||||||
|
|
||||||
// for (var i = 0; i < 6; i++)
|
|
||||||
// {
|
|
||||||
// var inverseI = 5 - i;
|
|
||||||
// var j = inverseI % 3 + 1;
|
|
||||||
// var k = inverseI / 3;
|
|
||||||
// g.DrawImage(GetImage(items[i], GetImageType.Item),
|
|
||||||
// new Rectangle(img.Width - (j * (smallImgSize + margin) + margin), 92 + k * (smallImgSize + margin),
|
|
||||||
// smallImgSize,
|
|
||||||
// smallImgSize));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// var cachedChamp = new CachedChampion { AddedAt = DateTime.UtcNow, ImageStream = img.ToStream(System.Drawing.Imaging.ImageFormat.Png), Name = name.ToLower() + "_" + resolvedRole };
|
|
||||||
// CachedChampionImages.Add(cachedChamp.Name, cachedChamp);
|
|
||||||
// await e.Channel.SendFile(data["title"] + "_stats.png", cachedChamp.ImageStream).ConfigureAwait(false);
|
|
||||||
// }
|
|
||||||
// catch (Exception ex)
|
|
||||||
// {
|
|
||||||
// await channel.SendMessageAsync("💢 Failed retreiving data for that champion.").ConfigureAwait(false);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private enum GetImageType
|
|
||||||
// {
|
|
||||||
// Champion,
|
|
||||||
// Item
|
|
||||||
// }
|
|
||||||
// private static Image GetImage(string id, GetImageType imageType = GetImageType.Champion)
|
|
||||||
// {
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// switch (imageType)
|
|
||||||
// {
|
|
||||||
// case GetImageType.Champion:
|
|
||||||
// return Image.FromFile($"data/lol/champions/{id}.png");
|
|
||||||
// case GetImageType.Item:
|
|
||||||
// default:
|
|
||||||
// return Image.FromFile($"data/lol/items/{id}.png");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// catch (Exception)
|
|
||||||
// {
|
|
||||||
// return Image.FromFile("data/lol/_ERROR.png");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private static string ResolvePos(string pos)
|
|
||||||
// {
|
|
||||||
// if (string.IsNullOrWhiteSpace(pos))
|
|
||||||
// return null;
|
|
||||||
// switch (pos.ToLowerInvariant())
|
|
||||||
// {
|
|
||||||
// case "m":
|
|
||||||
// case "mid":
|
|
||||||
// case "midorfeed":
|
|
||||||
// case "midd":
|
|
||||||
// case "middle":
|
|
||||||
// return "Middle";
|
|
||||||
// case "top":
|
|
||||||
// case "topp":
|
|
||||||
// case "t":
|
|
||||||
// case "toporfeed":
|
|
||||||
// return "Top";
|
|
||||||
// case "j":
|
|
||||||
// case "jun":
|
|
||||||
// case "jungl":
|
|
||||||
// case "jungle":
|
|
||||||
// return "Jungle";
|
|
||||||
// case "a":
|
|
||||||
// case "ad":
|
|
||||||
// case "adc":
|
|
||||||
// case "carry":
|
|
||||||
// case "ad carry":
|
|
||||||
// case "adcarry":
|
|
||||||
// case "c":
|
|
||||||
// return "ADC";
|
|
||||||
// case "s":
|
|
||||||
// case "sup":
|
|
||||||
// case "supp":
|
|
||||||
// case "support":
|
|
||||||
// return "Support";
|
|
||||||
// default:
|
|
||||||
// return pos;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
@ -38,6 +38,27 @@ namespace NadekoBot.Modules.Searches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
//[OwnerOnly]
|
||||||
|
//public async Task Obfuscate([Remainder] string txt)
|
||||||
|
//{
|
||||||
|
// var lastItem = "en";
|
||||||
|
// foreach (var item in _google.Languages.Except(new[] { "en" }).Where(x => x.Length < 4))
|
||||||
|
// {
|
||||||
|
// var txt2 = await _searches.Translate(lastItem + ">" + item, txt);
|
||||||
|
// await Context.Channel.EmbedAsync(new EmbedBuilder()
|
||||||
|
// .WithOkColor()
|
||||||
|
// .WithTitle(lastItem + ">" + item)
|
||||||
|
// .AddField("Input", txt)
|
||||||
|
// .AddField("Output", txt2));
|
||||||
|
// txt = txt2;
|
||||||
|
// await Task.Delay(500);
|
||||||
|
// lastItem = item;
|
||||||
|
// }
|
||||||
|
// txt = await _searches.Translate(lastItem + ">en", txt);
|
||||||
|
// await Context.Channel.SendConfirmAsync("Final output:\n\n" + txt);
|
||||||
|
//}
|
||||||
|
|
||||||
public enum AutoDeleteAutoTranslate
|
public enum AutoDeleteAutoTranslate
|
||||||
{
|
{
|
||||||
Del,
|
Del,
|
||||||
|
@ -207,7 +207,7 @@ namespace NadekoBot.Modules.Utility.Services
|
|||||||
if (guildSettings.TryRemove(guild.Id, out var setting) && cleanup)
|
if (guildSettings.TryRemove(guild.Id, out var setting) && cleanup)
|
||||||
await RescanUsers(guild).ConfigureAwait(false);
|
await RescanUsers(guild).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
//todo multiple rescans at the same time?
|
|
||||||
private async Task RescanUser(IGuildUser user, StreamRoleSettings setting, IRole addRole = null)
|
private async Task RescanUser(IGuildUser user, StreamRoleSettings setting, IRole addRole = null)
|
||||||
{
|
{
|
||||||
if (user.Game.HasValue &&
|
if (user.Game.HasValue &&
|
||||||
|
@ -417,7 +417,7 @@ namespace NadekoBot.Modules.Utility
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
await Context.User.SendFileAsync(
|
await Context.User.SendFileAsync(
|
||||||
await JsonConvert.SerializeObject(grouping, Formatting.Indented).ToStream().ConfigureAwait(false), title, title).ConfigureAwait(false);
|
await JsonConvert.SerializeObject(grouping, Formatting.Indented).ToStream().ConfigureAwait(false), title, title, false).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
public async Task Ping()
|
public async Task Ping()
|
||||||
|
311
src/NadekoBot/Modules/Xp/Club.cs
Normal file
311
src/NadekoBot/Modules/Xp/Club.cs
Normal file
@ -0,0 +1,311 @@
|
|||||||
|
using Discord;
|
||||||
|
using Discord.Commands;
|
||||||
|
using Discord.WebSocket;
|
||||||
|
using NadekoBot.Common.Attributes;
|
||||||
|
using NadekoBot.Extensions;
|
||||||
|
using NadekoBot.Modules.Xp.Common;
|
||||||
|
using NadekoBot.Modules.Xp.Services;
|
||||||
|
using NadekoBot.Services.Database.Models;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Xp
|
||||||
|
{
|
||||||
|
public partial class Xp
|
||||||
|
{
|
||||||
|
[Group]
|
||||||
|
public class Club : NadekoSubmodule<ClubService>
|
||||||
|
{
|
||||||
|
private readonly XpService _xps;
|
||||||
|
private readonly DiscordSocketClient _client;
|
||||||
|
|
||||||
|
public Club(XpService xps, DiscordSocketClient client)
|
||||||
|
{
|
||||||
|
_xps = xps;
|
||||||
|
_client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
public async Task ClubCreate([Remainder]string clubName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(clubName) || clubName.Length > 20)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_service.CreateClub(Context.User, clubName, out ClubInfo club))
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalized("club_create_error").ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ReplyConfirmLocalized("club_created", Format.Bold(club.ToString())).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
public Task ClubIcon([Remainder]string url = null)
|
||||||
|
{
|
||||||
|
if ((!Uri.IsWellFormedUriString(url, UriKind.Absolute) && url != null)
|
||||||
|
|| !_service.SetClubIcon(Context.User.Id, url))
|
||||||
|
{
|
||||||
|
return ReplyErrorLocalized("club_icon_error");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReplyConfirmLocalized("club_icon_set");
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[Priority(1)]
|
||||||
|
public async Task ClubInformation(IUser user = null)
|
||||||
|
{
|
||||||
|
user = user ?? Context.User;
|
||||||
|
var club = _service.GetClubByMember(user);
|
||||||
|
if (club == null)
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalized("club_not_exists").ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ClubInformation(club.ToString()).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[Priority(0)]
|
||||||
|
public async Task ClubInformation([Remainder]string clubName = null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(clubName))
|
||||||
|
{
|
||||||
|
await ClubInformation(Context.User).ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClubInfo club;
|
||||||
|
if (!_service.GetClubByName(clubName, out club))
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalized("club_not_exists").ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var lvl = new LevelStats(club.Xp);
|
||||||
|
|
||||||
|
await Context.Channel.SendPaginatedConfirmAsync(_client, 0, (page) =>
|
||||||
|
{
|
||||||
|
var embed = new EmbedBuilder()
|
||||||
|
.WithOkColor()
|
||||||
|
.WithTitle($"{club.ToString()}")
|
||||||
|
.WithDescription(GetText("level_x", lvl.Level) + $" ({club.Xp} xp)")
|
||||||
|
.AddField("Owner", club.Owner.ToString(), true)
|
||||||
|
.AddField("Level Req.", club.MinimumLevelReq.ToString(), true)
|
||||||
|
.AddField("Members", string.Join("\n", club.Users
|
||||||
|
.Skip(page * 10)
|
||||||
|
.Take(10)), false);
|
||||||
|
|
||||||
|
if (Uri.IsWellFormedUriString(club.ImageUrl, UriKind.Absolute))
|
||||||
|
return embed.WithThumbnailUrl(club.ImageUrl);
|
||||||
|
|
||||||
|
return embed;
|
||||||
|
}, club.Users.Count / 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
public Task ClubBans(int page = 1)
|
||||||
|
{
|
||||||
|
if (--page < 0)
|
||||||
|
return Task.CompletedTask;
|
||||||
|
|
||||||
|
var club = _service.GetBansAndApplications(Context.User.Id);
|
||||||
|
if (club == null)
|
||||||
|
return ReplyErrorLocalized("club_not_exists");
|
||||||
|
|
||||||
|
var bans = club
|
||||||
|
.Bans
|
||||||
|
.Select(x => x.User)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
return Context.Channel.SendPaginatedConfirmAsync(_client, page,
|
||||||
|
curPage =>
|
||||||
|
{
|
||||||
|
var toShow = string.Join("\n", bans
|
||||||
|
.Skip(page * 10)
|
||||||
|
.Take(10)
|
||||||
|
.Select(x => x.ToString()));
|
||||||
|
|
||||||
|
return new EmbedBuilder()
|
||||||
|
.WithTitle(GetText("club_bans_for", club.ToString()))
|
||||||
|
.WithDescription(toShow);
|
||||||
|
|
||||||
|
}, bans.Length / 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
public Task ClubApps(int page = 1)
|
||||||
|
{
|
||||||
|
if (--page < 0)
|
||||||
|
return Task.CompletedTask;
|
||||||
|
|
||||||
|
var club = _service.GetBansAndApplications(Context.User.Id);
|
||||||
|
if (club == null)
|
||||||
|
return ReplyErrorLocalized("club_not_exists");
|
||||||
|
|
||||||
|
var bans = club
|
||||||
|
.Applicants
|
||||||
|
.Select(x => x.User)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
return Context.Channel.SendPaginatedConfirmAsync(_client, page,
|
||||||
|
curPage =>
|
||||||
|
{
|
||||||
|
var toShow = string.Join("\n", bans
|
||||||
|
.Skip(page * 10)
|
||||||
|
.Take(10)
|
||||||
|
.Select(x => x.ToString()));
|
||||||
|
|
||||||
|
return new EmbedBuilder()
|
||||||
|
.WithTitle(GetText("club_apps_for", club.ToString()))
|
||||||
|
.WithDescription(toShow);
|
||||||
|
|
||||||
|
}, bans.Length / 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
public async Task ClubApply([Remainder]string clubName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(clubName))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_service.GetClubByName(clubName, out ClubInfo club))
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalized("club_not_exists").ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_service.ApplyToClub(Context.User, club))
|
||||||
|
{
|
||||||
|
await ReplyConfirmLocalized("club_applied", Format.Bold(club.ToString())).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalized("club_apply_error").ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[Priority(1)]
|
||||||
|
public Task ClubAccept(IUser user)
|
||||||
|
=> ClubAccept(user.ToString());
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[Priority(0)]
|
||||||
|
public async Task ClubAccept([Remainder]string userName)
|
||||||
|
{
|
||||||
|
if (_service.AcceptApplication(Context.User.Id, userName, out var discordUser))
|
||||||
|
{
|
||||||
|
await ReplyConfirmLocalized("club_accepted", Format.Bold(discordUser.ToString())).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
await ReplyErrorLocalized("club_accept_error").ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
public async Task Clubleave()
|
||||||
|
{
|
||||||
|
if (_service.LeaveClub(Context.User))
|
||||||
|
await ReplyConfirmLocalized("club_left").ConfigureAwait(false);
|
||||||
|
else
|
||||||
|
await ReplyErrorLocalized("club_not_in_club").ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[Priority(1)]
|
||||||
|
public Task ClubKick([Remainder]IUser user)
|
||||||
|
=> ClubKick(user.ToString());
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[Priority(0)]
|
||||||
|
public Task ClubKick([Remainder]string userName)
|
||||||
|
{
|
||||||
|
if (_service.Kick(Context.User.Id, userName, out var club))
|
||||||
|
return ReplyConfirmLocalized("club_user_kick", Format.Bold(userName), Format.Bold(club.ToString()));
|
||||||
|
else
|
||||||
|
return ReplyErrorLocalized("club_user_kick_fail");
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[Priority(1)]
|
||||||
|
public Task ClubBan([Remainder]IUser user)
|
||||||
|
=> ClubBan(user.ToString());
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[Priority(0)]
|
||||||
|
public Task ClubBan([Remainder]string userName)
|
||||||
|
{
|
||||||
|
if (_service.Ban(Context.User.Id, userName, out var club))
|
||||||
|
return ReplyConfirmLocalized("club_user_banned", Format.Bold(userName), Format.Bold(club.ToString()));
|
||||||
|
else
|
||||||
|
return ReplyErrorLocalized("club_user_ban_fail");
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[Priority(1)]
|
||||||
|
public Task ClubUnBan([Remainder]IUser user)
|
||||||
|
=> ClubUnBan(user.ToString());
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[Priority(0)]
|
||||||
|
public Task ClubUnBan([Remainder]string userName)
|
||||||
|
{
|
||||||
|
if (_service.UnBan(Context.User.Id, userName, out var club))
|
||||||
|
return ReplyConfirmLocalized("club_user_unbanned", Format.Bold(userName), Format.Bold(club.ToString()));
|
||||||
|
else
|
||||||
|
return ReplyErrorLocalized("club_user_unban_fail");
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
public async Task ClubLevelReq(int level)
|
||||||
|
{
|
||||||
|
if (_service.ChangeClubLevelReq(Context.User.Id, level))
|
||||||
|
{
|
||||||
|
await ReplyConfirmLocalized("club_level_req_changed", Format.Bold(level.ToString())).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalized("club_level_req_change_error").ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
public async Task ClubDisband()
|
||||||
|
{
|
||||||
|
if (_service.Disband(Context.User.Id, out ClubInfo club))
|
||||||
|
{
|
||||||
|
await ReplyConfirmLocalized("club_disbanded", Format.Bold(club.ToString())).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalized("club_disaband_error").ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
public Task ClubLeaderboard(int page = 1)
|
||||||
|
{
|
||||||
|
if (--page < 0)
|
||||||
|
return Task.CompletedTask;
|
||||||
|
|
||||||
|
var clubs = _service.GetClubLeaderboardPage(page);
|
||||||
|
|
||||||
|
var embed = new EmbedBuilder()
|
||||||
|
.WithTitle(GetText("club_leaderboard", page + 1))
|
||||||
|
.WithOkColor();
|
||||||
|
|
||||||
|
var i = page * 9;
|
||||||
|
foreach (var club in clubs)
|
||||||
|
{
|
||||||
|
embed.AddField($"#{++i} " + club.ToString(), club.Xp.ToString() + " xp", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Context.Channel.EmbedAsync(embed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
src/NadekoBot/Modules/Xp/Common/FullUserStats.cs
Normal file
26
src/NadekoBot/Modules/Xp/Common/FullUserStats.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using NadekoBot.Services.Database.Models;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Xp.Common
|
||||||
|
{
|
||||||
|
public class FullUserStats
|
||||||
|
{
|
||||||
|
public DiscordUser User { get; }
|
||||||
|
public UserXpStats FullGuildStats { get; }
|
||||||
|
public LevelStats Global { get; }
|
||||||
|
public LevelStats Guild { get; }
|
||||||
|
public int GlobalRanking { get; }
|
||||||
|
public int GuildRanking { get; }
|
||||||
|
|
||||||
|
public FullUserStats(DiscordUser usr,
|
||||||
|
UserXpStats fullGuildStats, LevelStats global,
|
||||||
|
LevelStats guild, int globalRanking, int guildRanking)
|
||||||
|
{
|
||||||
|
this.User = usr;
|
||||||
|
this.Global = global;
|
||||||
|
this.Guild = guild;
|
||||||
|
this.GlobalRanking = globalRanking;
|
||||||
|
this.GuildRanking = guildRanking;
|
||||||
|
this.FullGuildStats = fullGuildStats;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
43
src/NadekoBot/Modules/Xp/Common/LevelStats.cs
Normal file
43
src/NadekoBot/Modules/Xp/Common/LevelStats.cs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
using NadekoBot.Modules.Xp.Services;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Xp.Common
|
||||||
|
{
|
||||||
|
public class LevelStats
|
||||||
|
{
|
||||||
|
public int Level { get; }
|
||||||
|
public int LevelXp { get; }
|
||||||
|
public int RequiredXp { get; }
|
||||||
|
public int TotalXp { get; }
|
||||||
|
|
||||||
|
public LevelStats(int xp)
|
||||||
|
{
|
||||||
|
if (xp < 0)
|
||||||
|
xp = 0;
|
||||||
|
|
||||||
|
TotalXp = xp;
|
||||||
|
|
||||||
|
const int baseXp = XpService.XP_REQUIRED_LVL_1;
|
||||||
|
|
||||||
|
var required = baseXp;
|
||||||
|
var totalXp = 0;
|
||||||
|
var lvl = 1;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
required = (int)(baseXp + baseXp / 4.0 * (lvl - 1));
|
||||||
|
|
||||||
|
if (required + totalXp > xp)
|
||||||
|
break;
|
||||||
|
|
||||||
|
totalXp += required;
|
||||||
|
lvl++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Level = lvl - 1;
|
||||||
|
LevelXp = xp - totalXp;
|
||||||
|
RequiredXp = required;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LevelStats FromXp(int xp) => new LevelStats(xp);
|
||||||
|
}
|
||||||
|
}
|
34
src/NadekoBot/Modules/Xp/Extensions/Extensions.cs
Normal file
34
src/NadekoBot/Modules/Xp/Extensions/Extensions.cs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
using NadekoBot.Modules.Xp.Services;
|
||||||
|
using NadekoBot.Services.Database.Models;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Xp.Extensions
|
||||||
|
{
|
||||||
|
public static class Extensions
|
||||||
|
{
|
||||||
|
public static (int Level, int LevelXp, int LevelRequiredXp) GetLevelData(this UserXpStats stats)
|
||||||
|
{
|
||||||
|
var baseXp = XpService.XP_REQUIRED_LVL_1;
|
||||||
|
|
||||||
|
var required = baseXp;
|
||||||
|
var totalXp = 0;
|
||||||
|
var lvl = 1;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
required = (int)(baseXp + baseXp / 4.0 * (lvl - 1));
|
||||||
|
|
||||||
|
if (required + totalXp > stats.Xp)
|
||||||
|
break;
|
||||||
|
|
||||||
|
totalXp += required;
|
||||||
|
lvl++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (lvl - 1, stats.Xp - totalXp, required);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
312
src/NadekoBot/Modules/Xp/Services/ClubService.cs
Normal file
312
src/NadekoBot/Modules/Xp/Services/ClubService.cs
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
using NadekoBot.Services;
|
||||||
|
using System;
|
||||||
|
using NadekoBot.Services.Database.Models;
|
||||||
|
using Discord;
|
||||||
|
using NadekoBot.Modules.Xp.Common;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Xp.Services
|
||||||
|
{
|
||||||
|
public class ClubService : INService
|
||||||
|
{
|
||||||
|
private readonly DbService _db;
|
||||||
|
|
||||||
|
public ClubService(DbService db)
|
||||||
|
{
|
||||||
|
_db = db;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CreateClub(IUser user, string clubName, out ClubInfo club)
|
||||||
|
{
|
||||||
|
//must be lvl 5 and must not be in a club already
|
||||||
|
|
||||||
|
club = null;
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
var du = uow.DiscordUsers.GetOrCreate(user);
|
||||||
|
uow._context.SaveChanges();
|
||||||
|
var xp = new LevelStats(uow.Xp.GetTotalUserXp(user.Id));
|
||||||
|
|
||||||
|
if (xp.Level >= 5 && du.Club == null)
|
||||||
|
{
|
||||||
|
du.Club = new ClubInfo()
|
||||||
|
{
|
||||||
|
Name = clubName,
|
||||||
|
Discrim = uow.Clubs.GetNextDiscrim(clubName),
|
||||||
|
Owner = du,
|
||||||
|
};
|
||||||
|
uow.Clubs.Add(du.Club);
|
||||||
|
uow._context.SaveChanges();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uow._context.Set<ClubApplicants>()
|
||||||
|
.RemoveRange(uow._context.Set<ClubApplicants>().Where(x => x.UserId == du.Id));
|
||||||
|
club = du.Club;
|
||||||
|
uow.Complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClubInfo GetClubByMember(IUser user)
|
||||||
|
{
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
return uow.Clubs.GetByMember(user.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SetClubIcon(ulong ownerUserId, string url)
|
||||||
|
{
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
var club = uow.Clubs.GetByOwner(ownerUserId, set => set);
|
||||||
|
|
||||||
|
if (club == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
club.ImageUrl = url;
|
||||||
|
uow.Complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GetClubByName(string clubName, out ClubInfo club)
|
||||||
|
{
|
||||||
|
club = null;
|
||||||
|
var arr = clubName.Split('#');
|
||||||
|
if (arr.Length < 2 || !int.TryParse(arr[arr.Length - 1], out var discrim))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
//incase club has # in it
|
||||||
|
var name = string.Concat(arr.Except(new[] { arr[arr.Length - 1] }));
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(name))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
club = uow.Clubs.GetByName(name.Trim().ToLowerInvariant(), discrim);
|
||||||
|
if (club == null)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ApplyToClub(IUser user, ClubInfo club)
|
||||||
|
{
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
var du = uow.DiscordUsers.GetOrCreate(user);
|
||||||
|
uow._context.SaveChanges();
|
||||||
|
|
||||||
|
if (du.Club != null
|
||||||
|
|| new LevelStats(uow.Xp.GetTotalUserXp(user.Id)).Level < club.MinimumLevelReq
|
||||||
|
|| club.Bans.Any(x => x.UserId == du.Id)
|
||||||
|
|| club.Applicants.Any(x => x.UserId == du.Id))
|
||||||
|
{
|
||||||
|
//user banned or a member of a club, or already applied,
|
||||||
|
// or doesn't min minumum level requirement, can't apply
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var app = new ClubApplicants
|
||||||
|
{
|
||||||
|
ClubId = club.Id,
|
||||||
|
UserId = du.Id,
|
||||||
|
};
|
||||||
|
|
||||||
|
uow._context.Set<ClubApplicants>().Add(app);
|
||||||
|
|
||||||
|
uow.Complete();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool AcceptApplication(ulong clubOwnerUserId, string userName, out DiscordUser discordUser)
|
||||||
|
{
|
||||||
|
discordUser = null;
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
var club = uow.Clubs.GetByOwner(clubOwnerUserId,
|
||||||
|
set => set.Include(x => x.Applicants)
|
||||||
|
.ThenInclude(x => x.Club)
|
||||||
|
.Include(x => x.Applicants)
|
||||||
|
.ThenInclude(x => x.User));
|
||||||
|
if (club == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var applicant = club.Applicants.FirstOrDefault(x => x.User.ToString().ToLowerInvariant() == userName.ToLowerInvariant());
|
||||||
|
if (applicant == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
applicant.User.Club = club;
|
||||||
|
club.Applicants.Remove(applicant);
|
||||||
|
|
||||||
|
//remove that user's all other applications
|
||||||
|
uow._context.Set<ClubApplicants>()
|
||||||
|
.RemoveRange(uow._context.Set<ClubApplicants>().Where(x => x.UserId == applicant.User.Id));
|
||||||
|
|
||||||
|
discordUser = applicant.User;
|
||||||
|
uow.Complete();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClubInfo GetBansAndApplications(ulong ownerUserId)
|
||||||
|
{
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
return uow.Clubs.GetByOwner(ownerUserId,
|
||||||
|
x => x.Include(y => y.Bans)
|
||||||
|
.ThenInclude(y => y.User)
|
||||||
|
.Include(y => y.Applicants)
|
||||||
|
.ThenInclude(y => y.User));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool LeaveClub(IUser user)
|
||||||
|
{
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
var du = uow.DiscordUsers.GetOrCreate(user);
|
||||||
|
if (du.Club == null || du.Club.OwnerId == du.Id)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
du.Club = null;
|
||||||
|
uow.Complete();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ChangeClubLevelReq(ulong userId, int level)
|
||||||
|
{
|
||||||
|
if (level < 5)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
var club = uow.Clubs.GetByOwner(userId);
|
||||||
|
if (club == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
club.MinimumLevelReq = level;
|
||||||
|
uow.Complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Disband(ulong userId, out ClubInfo club)
|
||||||
|
{
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
club = uow.Clubs.GetByOwner(userId);
|
||||||
|
if (club == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uow.Clubs.Remove(club);
|
||||||
|
uow.Complete();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Ban(ulong ownerUserId, string userName, out ClubInfo club)
|
||||||
|
{
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
club = uow.Clubs.GetByOwner(ownerUserId,
|
||||||
|
set => set.Include(x => x.Applicants)
|
||||||
|
.ThenInclude(x => x.User));
|
||||||
|
if (club == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var usr = club.Users.FirstOrDefault(x => x.ToString().ToLowerInvariant() == userName.ToLowerInvariant())
|
||||||
|
?? club.Applicants.FirstOrDefault(x => x.User.ToString().ToLowerInvariant() == userName.ToLowerInvariant())?.User;
|
||||||
|
if (usr == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (club.OwnerId == usr.Id) // can't ban the owner kek, whew
|
||||||
|
return false;
|
||||||
|
|
||||||
|
club.Bans.Add(new ClubBans
|
||||||
|
{
|
||||||
|
Club = club,
|
||||||
|
User = usr,
|
||||||
|
});
|
||||||
|
club.Users.Remove(usr);
|
||||||
|
|
||||||
|
var app = club.Applicants.FirstOrDefault(x => x.UserId == usr.Id);
|
||||||
|
if (app != null)
|
||||||
|
club.Applicants.Remove(app);
|
||||||
|
|
||||||
|
uow.Complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool UnBan(ulong ownerUserId, string userName, out ClubInfo club)
|
||||||
|
{
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
club = uow.Clubs.GetByOwner(ownerUserId,
|
||||||
|
set => set.Include(x => x.Bans)
|
||||||
|
.ThenInclude(x => x.User));
|
||||||
|
if (club == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var ban = club.Bans.FirstOrDefault(x => x.User.ToString().ToLowerInvariant() == userName.ToLowerInvariant());
|
||||||
|
if (ban == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
club.Bans.Remove(ban);
|
||||||
|
uow.Complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Kick(ulong ownerUserId, string userName, out ClubInfo club)
|
||||||
|
{
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
club = uow.Clubs.GetByOwner(ownerUserId);
|
||||||
|
if (club == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var usr = club.Users.FirstOrDefault(x => x.ToString().ToLowerInvariant() == userName.ToLowerInvariant());
|
||||||
|
if (usr == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (club.OwnerId == usr.Id)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
club.Users.Remove(usr);
|
||||||
|
var app = club.Applicants.FirstOrDefault(x => x.UserId == usr.Id);
|
||||||
|
if (app != null)
|
||||||
|
club.Applicants.Remove(app);
|
||||||
|
uow.Complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClubInfo[] GetClubLeaderboardPage(int page)
|
||||||
|
{
|
||||||
|
if (page < 0)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(page));
|
||||||
|
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
return uow.Clubs.GetClubLeaderboardPage(page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
src/NadekoBot/Modules/Xp/Services/UserCacheItem.cs
Normal file
21
src/NadekoBot/Modules/Xp/Services/UserCacheItem.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using Discord;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Xp.Services
|
||||||
|
{
|
||||||
|
public class UserCacheItem
|
||||||
|
{
|
||||||
|
public IGuildUser User { get; set; }
|
||||||
|
public IGuild Guild { get; set; }
|
||||||
|
public IMessageChannel Channel { get; set; }
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return User.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is UserCacheItem uci && uci.User == User;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
731
src/NadekoBot/Modules/Xp/Services/XpService.cs
Normal file
731
src/NadekoBot/Modules/Xp/Services/XpService.cs
Normal file
@ -0,0 +1,731 @@
|
|||||||
|
using Discord;
|
||||||
|
using Discord.WebSocket;
|
||||||
|
using NadekoBot.Common.Collections;
|
||||||
|
using NadekoBot.Extensions;
|
||||||
|
using NadekoBot.Modules.Xp.Common;
|
||||||
|
using NadekoBot.Services;
|
||||||
|
using NadekoBot.Services.Database.Models;
|
||||||
|
using NadekoBot.Services.Impl;
|
||||||
|
using NLog;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using ImageSharp;
|
||||||
|
using Image = ImageSharp.Image;
|
||||||
|
using SixLabors.Fonts;
|
||||||
|
using System.IO;
|
||||||
|
using SixLabors.Primitives;
|
||||||
|
using System.Net.Http;
|
||||||
|
using SixLabors.Shapes;
|
||||||
|
using System.Numerics;
|
||||||
|
using ImageSharp.Drawing.Pens;
|
||||||
|
using ImageSharp.Drawing.Brushes;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Xp.Services
|
||||||
|
{
|
||||||
|
public class XpService : INService
|
||||||
|
{
|
||||||
|
private enum NotifOf { Server, Global } // is it a server level-up or global level-up notification
|
||||||
|
|
||||||
|
private readonly DbService _db;
|
||||||
|
private readonly CommandHandler _cmd;
|
||||||
|
private readonly IBotConfigProvider _bc;
|
||||||
|
private readonly IImagesService _images;
|
||||||
|
private readonly Logger _log;
|
||||||
|
private readonly NadekoStrings _strings;
|
||||||
|
private readonly FontCollection _fonts = new FontCollection();
|
||||||
|
public const int XP_REQUIRED_LVL_1 = 36;
|
||||||
|
|
||||||
|
private readonly ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> _excludedRoles
|
||||||
|
= new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>();
|
||||||
|
|
||||||
|
private readonly ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> _excludedChannels
|
||||||
|
= new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>();
|
||||||
|
|
||||||
|
private readonly ConcurrentHashSet<ulong> _excludedServers
|
||||||
|
= new ConcurrentHashSet<ulong>();
|
||||||
|
|
||||||
|
private readonly ConcurrentHashSet<ulong> _rewardedUsers
|
||||||
|
= new ConcurrentHashSet<ulong>();
|
||||||
|
|
||||||
|
private readonly ConcurrentQueue<UserCacheItem> _addMessageXp
|
||||||
|
= new ConcurrentQueue<UserCacheItem>();
|
||||||
|
|
||||||
|
private readonly ConcurrentDictionary<string, byte[]> _imageStreams
|
||||||
|
= new ConcurrentDictionary<string, byte[]>();
|
||||||
|
|
||||||
|
private readonly Timer updateXpTimer;
|
||||||
|
private readonly HttpClient http = new HttpClient();
|
||||||
|
private FontFamily _usernameFontFamily;
|
||||||
|
private FontFamily _clubFontFamily;
|
||||||
|
private Font _levelFont;
|
||||||
|
private Font _xpFont;
|
||||||
|
private Font _awardedFont;
|
||||||
|
private Font _rankFont;
|
||||||
|
private Font _timeFont;
|
||||||
|
|
||||||
|
public XpService(CommandHandler cmd, IBotConfigProvider bc,
|
||||||
|
IEnumerable<GuildConfig> allGuildConfigs, IImagesService images,
|
||||||
|
DbService db, NadekoStrings strings)
|
||||||
|
{
|
||||||
|
_db = db;
|
||||||
|
_cmd = cmd;
|
||||||
|
_bc = bc;
|
||||||
|
_images = images;
|
||||||
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
|
_strings = strings;
|
||||||
|
|
||||||
|
//todo 60 move to font provider or somethign
|
||||||
|
_fonts = new FontCollection();
|
||||||
|
if (Directory.Exists("data/fonts"))
|
||||||
|
foreach (var file in Directory.GetFiles("data/fonts"))
|
||||||
|
{
|
||||||
|
_fonts.Install(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
InitializeFonts();
|
||||||
|
|
||||||
|
_cmd.OnMessageNoTrigger += _cmd_OnMessageNoTrigger;
|
||||||
|
|
||||||
|
updateXpTimer = new Timer(async _ =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var toNotify = new List<(IMessageChannel MessageChannel, IUser User, int Level, XpNotificationType NotifyType, NotifOf NotifOf)>();
|
||||||
|
var roleRewards = new Dictionary<ulong, List<XpRoleReward>>();
|
||||||
|
|
||||||
|
var toAddTo = new List<UserCacheItem>();
|
||||||
|
while (_addMessageXp.TryDequeue(out var usr))
|
||||||
|
toAddTo.Add(usr);
|
||||||
|
|
||||||
|
var group = toAddTo.GroupBy(x => (GuildId: x.Guild.Id, User: x.User));
|
||||||
|
if (toAddTo.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_log.Info("Adding XP to {0} users.", toAddTo.Count);
|
||||||
|
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
foreach (var item in group)
|
||||||
|
{
|
||||||
|
var xp = item.Select(x => bc.BotConfig.XpPerMessage).Sum();
|
||||||
|
|
||||||
|
var usr = uow.Xp.GetOrCreateUser(item.Key.GuildId, item.Key.User.Id);
|
||||||
|
var du = uow.DiscordUsers.GetOrCreate(item.Key.User);
|
||||||
|
|
||||||
|
var globalXp = uow.Xp.GetTotalUserXp(item.Key.User.Id);
|
||||||
|
var oldGlobalLevelData = new LevelStats(globalXp);
|
||||||
|
var newGlobalLevelData = new LevelStats(globalXp + xp);
|
||||||
|
|
||||||
|
var oldGuildLevelData = new LevelStats(usr.Xp + usr.AwardedXp);
|
||||||
|
usr.Xp += xp;
|
||||||
|
if (du.Club != null)
|
||||||
|
du.Club.Xp += xp;
|
||||||
|
var newGuildLevelData = new LevelStats(usr.Xp + usr.AwardedXp);
|
||||||
|
|
||||||
|
if (oldGlobalLevelData.Level < newGlobalLevelData.Level)
|
||||||
|
{
|
||||||
|
du.LastLevelUp = DateTime.UtcNow;
|
||||||
|
var first = item.First();
|
||||||
|
if (du.NotifyOnLevelUp != XpNotificationType.None)
|
||||||
|
toNotify.Add((first.Channel, first.User, newGlobalLevelData.Level, du.NotifyOnLevelUp, NotifOf.Global));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldGuildLevelData.Level < newGuildLevelData.Level)
|
||||||
|
{
|
||||||
|
usr.LastLevelUp = DateTime.UtcNow;
|
||||||
|
//send level up notification
|
||||||
|
var first = item.First();
|
||||||
|
if (usr.NotifyOnLevelUp != XpNotificationType.None)
|
||||||
|
toNotify.Add((first.Channel, first.User, newGuildLevelData.Level, usr.NotifyOnLevelUp, NotifOf.Server));
|
||||||
|
|
||||||
|
//give role
|
||||||
|
if (!roleRewards.TryGetValue(usr.GuildId, out var rewards))
|
||||||
|
{
|
||||||
|
rewards = uow.GuildConfigs.XpSettingsFor(usr.GuildId).RoleRewards.ToList();
|
||||||
|
roleRewards.Add(usr.GuildId, rewards);
|
||||||
|
}
|
||||||
|
|
||||||
|
var rew = rewards.FirstOrDefault(x => x.Level == newGuildLevelData.Level);
|
||||||
|
if (rew != null)
|
||||||
|
{
|
||||||
|
var role = first.User.Guild.GetRole(rew.RoleId);
|
||||||
|
if (role != null)
|
||||||
|
{
|
||||||
|
var __ = first.User.AddRoleAsync(role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uow.Complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.WhenAll(toNotify.Select(async x =>
|
||||||
|
{
|
||||||
|
if (x.NotifOf == NotifOf.Server)
|
||||||
|
{
|
||||||
|
if (x.NotifyType == XpNotificationType.Dm)
|
||||||
|
{
|
||||||
|
var chan = await x.User.GetOrCreateDMChannelAsync().ConfigureAwait(false);
|
||||||
|
if (chan != null)
|
||||||
|
await chan.SendConfirmAsync(_strings.GetText("level_up_dm",
|
||||||
|
(x.MessageChannel as ITextChannel)?.GuildId,
|
||||||
|
"xp",
|
||||||
|
x.User.Mention, Format.Bold(x.Level.ToString()),
|
||||||
|
Format.Bold((x.MessageChannel as ITextChannel)?.Guild.ToString() ?? "-")))
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
else // channel
|
||||||
|
{
|
||||||
|
await x.MessageChannel.SendConfirmAsync(_strings.GetText("level_up_channel",
|
||||||
|
(x.MessageChannel as ITextChannel)?.GuildId,
|
||||||
|
"xp",
|
||||||
|
x.User.Mention, Format.Bold(x.Level.ToString())))
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IMessageChannel chan;
|
||||||
|
if (x.NotifyType == XpNotificationType.Dm)
|
||||||
|
{
|
||||||
|
chan = await x.User.GetOrCreateDMChannelAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
else // channel
|
||||||
|
{
|
||||||
|
chan = x.MessageChannel;
|
||||||
|
}
|
||||||
|
await chan.SendConfirmAsync(_strings.GetText("level_up_global",
|
||||||
|
(x.MessageChannel as ITextChannel)?.GuildId,
|
||||||
|
"xp",
|
||||||
|
x.User.Mention, Format.Bold(x.Level.ToString())))
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_log.Warn(ex);
|
||||||
|
}
|
||||||
|
}, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5));
|
||||||
|
|
||||||
|
var clearRewardTimer = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
_rewardedUsers.Clear();
|
||||||
|
|
||||||
|
await Task.Delay(TimeSpan.FromMinutes(_bc.BotConfig.XpMinutesTimeout));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<XpRoleReward> GetRoleRewards(ulong id)
|
||||||
|
{
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
return uow.GuildConfigs.XpSettingsFor(id)
|
||||||
|
.RoleRewards
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetRoleReward(ulong guildId, int level, ulong? roleId)
|
||||||
|
{
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
var settings = uow.GuildConfigs.XpSettingsFor(guildId);
|
||||||
|
|
||||||
|
if (roleId == null)
|
||||||
|
{
|
||||||
|
settings.RoleRewards.RemoveWhere(x => x.Level == level);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var rew = settings.RoleRewards.FirstOrDefault(x => x.Level == level);
|
||||||
|
|
||||||
|
if (rew != null)
|
||||||
|
rew.RoleId = roleId.Value;
|
||||||
|
else
|
||||||
|
settings.RoleRewards.Add(new XpRoleReward()
|
||||||
|
{
|
||||||
|
Level = level,
|
||||||
|
RoleId = roleId.Value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
uow.Complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserXpStats[] GetUserXps(ulong guildId, int page)
|
||||||
|
{
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
return uow.Xp.GetUsersFor(guildId, page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public (ulong UserId, int TotalXp)[] GetUserXps(int page)
|
||||||
|
{
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
return uow.Xp.GetUsersFor(page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ChangeNotificationType(ulong userId, ulong guildId, XpNotificationType type)
|
||||||
|
{
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
var user = uow.Xp.GetOrCreateUser(guildId, userId);
|
||||||
|
user.NotifyOnLevelUp = type;
|
||||||
|
await uow.CompleteAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ChangeNotificationType(IUser user, XpNotificationType type)
|
||||||
|
{
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
var du = uow.DiscordUsers.GetOrCreate(user);
|
||||||
|
du.NotifyOnLevelUp = type;
|
||||||
|
await uow.CompleteAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task _cmd_OnMessageNoTrigger(IUserMessage arg)
|
||||||
|
{
|
||||||
|
if (!(arg.Author is SocketGuildUser user) || user.IsBot)
|
||||||
|
return Task.CompletedTask;
|
||||||
|
|
||||||
|
var _ = Task.Run(() =>
|
||||||
|
{
|
||||||
|
if (!SetUserRewarded(user.Id))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_excludedChannels.TryGetValue(user.Guild.Id, out var chans) &&
|
||||||
|
chans.Contains(arg.Channel.Id))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_excludedServers.Contains(user.Guild.Id))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_excludedRoles.TryGetValue(user.Guild.Id, out var roles) &&
|
||||||
|
user.Roles.Any(x => roles.Contains(x.Id)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!arg.Content.Contains(' ') && arg.Content.Length < 5)
|
||||||
|
return;
|
||||||
|
_addMessageXp.Enqueue(new UserCacheItem { Guild = user.Guild, Channel = arg.Channel, User = user });
|
||||||
|
});
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddXp(ulong userId, ulong guildId, int amount)
|
||||||
|
{
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
var usr = uow.Xp.GetOrCreateUser(guildId, userId);
|
||||||
|
|
||||||
|
usr.AwardedXp += amount;
|
||||||
|
|
||||||
|
uow.Complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsServerExcluded(ulong id)
|
||||||
|
{
|
||||||
|
return _excludedServers.Contains(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<ulong> GetExcludedRoles(ulong id)
|
||||||
|
{
|
||||||
|
if (_excludedRoles.TryGetValue(id, out var val))
|
||||||
|
return val.ToArray();
|
||||||
|
|
||||||
|
return Enumerable.Empty<ulong>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<ulong> GetExcludedChannels(ulong id)
|
||||||
|
{
|
||||||
|
if (_excludedChannels.TryGetValue(id, out var val))
|
||||||
|
return val.ToArray();
|
||||||
|
|
||||||
|
return Enumerable.Empty<ulong>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool SetUserRewarded(ulong userId)
|
||||||
|
{
|
||||||
|
return _rewardedUsers.Add(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LevelStats GetGlobalUserStats(ulong userId)
|
||||||
|
{
|
||||||
|
int totalXp;
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
totalXp = uow.Xp.GetTotalUserXp(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new LevelStats(totalXp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FullUserStats GetUserStats(IGuildUser user)
|
||||||
|
{
|
||||||
|
DiscordUser du;
|
||||||
|
UserXpStats stats;
|
||||||
|
int totalXp;
|
||||||
|
int globalRank;
|
||||||
|
int guildRank;
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
du = uow.DiscordUsers.GetOrCreate(user);
|
||||||
|
stats = uow.Xp.GetOrCreateUser(user.GuildId, user.Id);
|
||||||
|
totalXp = uow.Xp.GetTotalUserXp(user.Id);
|
||||||
|
globalRank = uow.Xp.GetUserGlobalRanking(user.Id);
|
||||||
|
guildRank = uow.Xp.GetUserGuildRanking(user.Id, user.GuildId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FullUserStats(du,
|
||||||
|
stats,
|
||||||
|
new LevelStats(totalXp),
|
||||||
|
new LevelStats(stats.Xp + stats.AwardedXp),
|
||||||
|
globalRank,
|
||||||
|
guildRank);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static (int Level, int LevelXp, int LevelRequiredXp) GetLevelData(UserXpStats stats)
|
||||||
|
{
|
||||||
|
var baseXp = XpService.XP_REQUIRED_LVL_1;
|
||||||
|
|
||||||
|
var required = baseXp;
|
||||||
|
var totalXp = 0;
|
||||||
|
var lvl = 1;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
required = (int)(baseXp + baseXp / 4.0 * (lvl - 1));
|
||||||
|
|
||||||
|
if (required + totalXp > stats.Xp)
|
||||||
|
break;
|
||||||
|
|
||||||
|
totalXp += required;
|
||||||
|
lvl++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (lvl - 1, stats.Xp - totalXp, required);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ToggleExcludeServer(ulong id)
|
||||||
|
{
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
var xpSetting = uow.GuildConfigs.XpSettingsFor(id);
|
||||||
|
if (_excludedServers.Add(id))
|
||||||
|
{
|
||||||
|
xpSetting.ServerExcluded = true;
|
||||||
|
uow.Complete();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_excludedServers.TryRemove(id);
|
||||||
|
xpSetting.ServerExcluded = false;
|
||||||
|
uow.Complete();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ToggleExcludeRole(ulong guildId, ulong rId)
|
||||||
|
{
|
||||||
|
var roles = _excludedRoles.GetOrAdd(guildId, _ => new ConcurrentHashSet<ulong>());
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
var xpSetting = uow.GuildConfigs.XpSettingsFor(guildId);
|
||||||
|
var excludeObj = new ExcludedItem
|
||||||
|
{
|
||||||
|
ItemId = rId,
|
||||||
|
ItemType = ExcludedItemType.Role,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (roles.Add(rId))
|
||||||
|
{
|
||||||
|
|
||||||
|
if (xpSetting.ExclusionList.Add(excludeObj))
|
||||||
|
{
|
||||||
|
uow.Complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
roles.TryRemove(rId);
|
||||||
|
|
||||||
|
if (xpSetting.ExclusionList.Remove(excludeObj))
|
||||||
|
{
|
||||||
|
uow.Complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ToggleExcludeChannel(ulong guildId, ulong chId)
|
||||||
|
{
|
||||||
|
var channels = _excludedChannels.GetOrAdd(guildId, _ => new ConcurrentHashSet<ulong>());
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
var xpSetting = uow.GuildConfigs.XpSettingsFor(guildId);
|
||||||
|
var excludeObj = new ExcludedItem
|
||||||
|
{
|
||||||
|
ItemId = chId,
|
||||||
|
ItemType = ExcludedItemType.Channel,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (channels.Add(chId))
|
||||||
|
{
|
||||||
|
|
||||||
|
if (xpSetting.ExclusionList.Add(excludeObj))
|
||||||
|
{
|
||||||
|
uow.Complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
channels.TryRemove(chId);
|
||||||
|
|
||||||
|
if (xpSetting.ExclusionList.Remove(excludeObj))
|
||||||
|
{
|
||||||
|
uow.Complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<Image<Rgba32>> GenerateImageAsync(IGuildUser user)
|
||||||
|
{
|
||||||
|
return GenerateImageAsync(GetUserStats(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeFonts()
|
||||||
|
{
|
||||||
|
_usernameFontFamily = _fonts.Find("Whitney-Bold");
|
||||||
|
_clubFontFamily = _fonts.Find("Whitney-Bold");
|
||||||
|
_levelFont = _fonts.Find("Whitney-Bold").CreateFont(45);
|
||||||
|
_xpFont = _fonts.Find("Whitney-Bold").CreateFont(50);
|
||||||
|
_awardedFont = _fonts.Find("Whitney-Bold").CreateFont(25);
|
||||||
|
_rankFont = _fonts.Find("Uni Sans Thin CAPS").CreateFont(30);
|
||||||
|
_timeFont = _fonts.Find("Whitney-Bold").CreateFont(20);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Image<Rgba32>> GenerateImageAsync(FullUserStats stats)
|
||||||
|
{
|
||||||
|
var img = Image.Load(_images.XpCard.ToArray());
|
||||||
|
|
||||||
|
var username = stats.User.ToString();
|
||||||
|
var usernameFont = _usernameFontFamily
|
||||||
|
.CreateFont(username.Length <= 6
|
||||||
|
? 50
|
||||||
|
: 50 - username.Length);
|
||||||
|
|
||||||
|
img.DrawText("@" + username, usernameFont, Rgba32.White,
|
||||||
|
new PointF(130, 5));
|
||||||
|
|
||||||
|
// level
|
||||||
|
|
||||||
|
img.DrawText(stats.Global.Level.ToString(), _levelFont, Rgba32.White,
|
||||||
|
new PointF(47, 137));
|
||||||
|
|
||||||
|
img.DrawText(stats.Guild.Level.ToString(), _levelFont, Rgba32.White,
|
||||||
|
new PointF(47, 285));
|
||||||
|
|
||||||
|
//club name
|
||||||
|
|
||||||
|
var clubName = stats.User.Club?.ToString() ?? "-";
|
||||||
|
|
||||||
|
var clubFont = _clubFontFamily
|
||||||
|
.CreateFont(clubName.Length <= 8
|
||||||
|
? 35
|
||||||
|
: 35 - (clubName.Length / 2));
|
||||||
|
|
||||||
|
img.DrawText(clubName, clubFont, Rgba32.White,
|
||||||
|
new PointF(650 - clubName.Length * 10, 40));
|
||||||
|
|
||||||
|
var pen = new Pen<Rgba32>(Rgba32.Black, 1);
|
||||||
|
var brush = Brushes.Solid<Rgba32>(Rgba32.White);
|
||||||
|
var xpBgBrush = Brushes.Solid<Rgba32>(new Rgba32(0, 0, 0, 0.4f));
|
||||||
|
|
||||||
|
var global = stats.Global;
|
||||||
|
var guild = stats.Guild;
|
||||||
|
|
||||||
|
//xp bar
|
||||||
|
|
||||||
|
img.FillPolygon(xpBgBrush, new[] {
|
||||||
|
new PointF(321, 104),
|
||||||
|
new PointF(321 + (450 * (global.LevelXp / (float)global.RequiredXp)), 104),
|
||||||
|
new PointF(286 + (450 * (global.LevelXp / (float)global.RequiredXp)), 235),
|
||||||
|
new PointF(286, 235),
|
||||||
|
});
|
||||||
|
img.DrawText($"{global.LevelXp}/{global.RequiredXp}", _xpFont, brush, pen,
|
||||||
|
new PointF(430, 130));
|
||||||
|
|
||||||
|
img.FillPolygon(xpBgBrush, new[] {
|
||||||
|
new PointF(282, 248),
|
||||||
|
new PointF(282 + (450 * (guild.LevelXp / (float)guild.RequiredXp)), 248),
|
||||||
|
new PointF(247 + (450 * (guild.LevelXp / (float)guild.RequiredXp)), 379),
|
||||||
|
new PointF(247, 379),
|
||||||
|
});
|
||||||
|
img.DrawText($"{guild.LevelXp}/{guild.RequiredXp}", _xpFont, brush, pen,
|
||||||
|
new PointF(400, 270));
|
||||||
|
|
||||||
|
if (stats.FullGuildStats.AwardedXp != 0)
|
||||||
|
{
|
||||||
|
var sign = stats.FullGuildStats.AwardedXp > 0
|
||||||
|
? "+ "
|
||||||
|
: "";
|
||||||
|
img.DrawText($"({sign}{stats.FullGuildStats.AwardedXp})", _awardedFont, brush, pen,
|
||||||
|
new PointF(445 - (Math.Max(0, (stats.FullGuildStats.AwardedXp.ToString().Length - 2)) * 5), 335));
|
||||||
|
}
|
||||||
|
|
||||||
|
//ranking
|
||||||
|
|
||||||
|
img.DrawText(stats.GlobalRanking.ToString(), _rankFont, Rgba32.White,
|
||||||
|
new PointF(148, 170));
|
||||||
|
|
||||||
|
img.DrawText(stats.GuildRanking.ToString(), _rankFont, Rgba32.White,
|
||||||
|
new PointF(148, 317));
|
||||||
|
|
||||||
|
//time on this level
|
||||||
|
|
||||||
|
string GetTimeSpent(DateTime time)
|
||||||
|
{
|
||||||
|
var offset = DateTime.UtcNow - time;
|
||||||
|
return $"{offset.Days}d{offset.Hours}h{offset.Minutes}m";
|
||||||
|
}
|
||||||
|
|
||||||
|
img.DrawText(GetTimeSpent(stats.User.LastLevelUp), _timeFont, Rgba32.White,
|
||||||
|
new PointF(50, 197));
|
||||||
|
|
||||||
|
img.DrawText(GetTimeSpent(stats.FullGuildStats.LastLevelUp), _timeFont, Rgba32.White,
|
||||||
|
new PointF(50, 344));
|
||||||
|
|
||||||
|
//avatar
|
||||||
|
|
||||||
|
if (stats.User.AvatarId != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var avatarUrl = stats.User.RealAvatarUrl();
|
||||||
|
|
||||||
|
byte[] s;
|
||||||
|
if (!_imageStreams.TryGetValue(avatarUrl, out s))
|
||||||
|
{
|
||||||
|
using (var temp = await http.GetStreamAsync(avatarUrl))
|
||||||
|
{
|
||||||
|
var tempDraw = Image.Load(temp);
|
||||||
|
tempDraw = tempDraw.Resize(69, 70);
|
||||||
|
ApplyRoundedCorners(tempDraw, 35);
|
||||||
|
s = tempDraw.ToStream().ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
_imageStreams.AddOrUpdate(avatarUrl, s, (k, v) => s);
|
||||||
|
}
|
||||||
|
var toDraw = Image.Load(s);
|
||||||
|
|
||||||
|
|
||||||
|
img.DrawImage(toDraw,
|
||||||
|
1,
|
||||||
|
new Size(69, 70),
|
||||||
|
new Point(32, 10));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_log.Warn(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//club image
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(stats.User.Club?.ImageUrl))
|
||||||
|
{
|
||||||
|
var imgUrl = stats.User.Club.ImageUrl;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
byte[] s;
|
||||||
|
if (!_imageStreams.TryGetValue(imgUrl, out s))
|
||||||
|
{
|
||||||
|
using (var temp = await http.GetStreamAsync(imgUrl))
|
||||||
|
{
|
||||||
|
var tempDraw = Image.Load(temp);
|
||||||
|
tempDraw = tempDraw.Resize(45, 45);
|
||||||
|
ApplyRoundedCorners(tempDraw, 22.5f);
|
||||||
|
s = tempDraw.ToStream().ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
_imageStreams.AddOrUpdate(imgUrl, s, (k, v) => s);
|
||||||
|
}
|
||||||
|
var toDraw = Image.Load(s);
|
||||||
|
|
||||||
|
img.DrawImage(toDraw,
|
||||||
|
1,
|
||||||
|
new Size(45, 45),
|
||||||
|
new Point(722, 25));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_log.Warn(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var arr = img.ToStream().ToArray();
|
||||||
|
|
||||||
|
//_log.Info("{0:F2} KB", arr.Length * 1.0f / 1.KB());
|
||||||
|
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// https://github.com/SixLabors/ImageSharp/tree/master/samples/AvatarWithRoundedCorner
|
||||||
|
public static void ApplyRoundedCorners(Image<Rgba32> img, float cornerRadius)
|
||||||
|
{
|
||||||
|
var corners = BuildCorners(img.Width, img.Height, cornerRadius);
|
||||||
|
// now we have our corners time to draw them
|
||||||
|
img.Fill(Rgba32.Transparent, corners, new GraphicsOptions(true)
|
||||||
|
{
|
||||||
|
BlenderMode = ImageSharp.PixelFormats.PixelBlenderMode.Src // enforces that any part of this shape that has color is punched out of the background
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IPathCollection BuildCorners(int imageWidth, int imageHeight, float cornerRadius)
|
||||||
|
{
|
||||||
|
// first create a square
|
||||||
|
var rect = new RectangularePolygon(-0.5f, -0.5f, cornerRadius, cornerRadius);
|
||||||
|
|
||||||
|
// then cut out of the square a circle so we are left with a corner
|
||||||
|
var cornerToptLeft = rect.Clip(new EllipsePolygon(cornerRadius - 0.5f, cornerRadius - 0.5f, cornerRadius));
|
||||||
|
|
||||||
|
// corner is now a corner shape positions top left
|
||||||
|
//lets make 3 more positioned correctly, we can do that by translating the orgional around the center of the image
|
||||||
|
var center = new Vector2(imageWidth / 2, imageHeight / 2);
|
||||||
|
|
||||||
|
float rightPos = imageWidth - cornerToptLeft.Bounds.Width + 1;
|
||||||
|
float bottomPos = imageHeight - cornerToptLeft.Bounds.Height + 1;
|
||||||
|
|
||||||
|
// move it across the width of the image - the width of the shape
|
||||||
|
var cornerTopRight = cornerToptLeft.RotateDegree(90).Translate(rightPos, 0);
|
||||||
|
var cornerBottomLeft = cornerToptLeft.RotateDegree(-90).Translate(0, bottomPos);
|
||||||
|
var cornerBottomRight = cornerToptLeft.RotateDegree(180).Translate(rightPos, bottomPos);
|
||||||
|
|
||||||
|
return new PathCollection(cornerToptLeft, cornerBottomLeft, cornerTopRight, cornerBottomRight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
251
src/NadekoBot/Modules/Xp/Xp.cs
Normal file
251
src/NadekoBot/Modules/Xp/Xp.cs
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
using Discord;
|
||||||
|
using Discord.Commands;
|
||||||
|
using Discord.WebSocket;
|
||||||
|
using NadekoBot.Common.Attributes;
|
||||||
|
using NadekoBot.Extensions;
|
||||||
|
using NadekoBot.Modules.Xp.Common;
|
||||||
|
using NadekoBot.Modules.Xp.Services;
|
||||||
|
using NadekoBot.Services.Database.Models;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Xp
|
||||||
|
{
|
||||||
|
public partial class Xp : NadekoTopLevelModule<XpService>
|
||||||
|
{
|
||||||
|
private readonly DiscordSocketClient _client;
|
||||||
|
|
||||||
|
public Xp(DiscordSocketClient client)
|
||||||
|
{
|
||||||
|
_client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task Experience([Remainder]IUser user = null)
|
||||||
|
{
|
||||||
|
user = user ?? Context.User;
|
||||||
|
|
||||||
|
await Context.Channel.TriggerTypingAsync();
|
||||||
|
var img = await _service.GenerateImageAsync((IGuildUser)user);
|
||||||
|
|
||||||
|
await Context.Channel.SendFileAsync(img.ToStream(), $"{user.Id}_xp.png")
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public Task XpRoleRewards(int page = 1)
|
||||||
|
{
|
||||||
|
page--;
|
||||||
|
|
||||||
|
if (page < 0)
|
||||||
|
return Task.CompletedTask;
|
||||||
|
|
||||||
|
var roles = _service.GetRoleRewards(Context.Guild.Id)
|
||||||
|
.OrderBy(x => x.Level)
|
||||||
|
.Skip(page * 9)
|
||||||
|
.Take(9);
|
||||||
|
|
||||||
|
var embed = new EmbedBuilder()
|
||||||
|
.WithTitle(GetText("role_rewards"))
|
||||||
|
.WithOkColor();
|
||||||
|
|
||||||
|
if (!roles.Any())
|
||||||
|
return Context.Channel.EmbedAsync(embed.WithDescription(GetText("no_role_rewards")));
|
||||||
|
|
||||||
|
foreach (var rolerew in roles)
|
||||||
|
{
|
||||||
|
var role = Context.Guild.GetRole(rolerew.RoleId);
|
||||||
|
|
||||||
|
if (role == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
embed.AddField(GetText("level_x", Format.Bold(rolerew.Level.ToString())), role.ToString());
|
||||||
|
}
|
||||||
|
return Context.Channel.EmbedAsync(embed);
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[RequireUserPermission(GuildPermission.ManageRoles)]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task XpRoleReward(int level, [Remainder] IRole role = null)
|
||||||
|
{
|
||||||
|
if (level < 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_service.SetRoleReward(Context.Guild.Id, level, role?.Id);
|
||||||
|
|
||||||
|
if(role == null)
|
||||||
|
await ReplyConfirmLocalized("role_reward_cleared", level).ConfigureAwait(false);
|
||||||
|
else
|
||||||
|
await ReplyConfirmLocalized("role_reward_added", level, Format.Bold(role.ToString())).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum NotifyPlace
|
||||||
|
{
|
||||||
|
Server = 0,
|
||||||
|
Guild = 0,
|
||||||
|
Global = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task XpNotify(NotifyPlace place = NotifyPlace.Guild, XpNotificationType type = XpNotificationType.Channel)
|
||||||
|
{
|
||||||
|
if (place == NotifyPlace.Guild)
|
||||||
|
await _service.ChangeNotificationType(Context.User.Id, Context.Guild.Id, type);
|
||||||
|
else
|
||||||
|
await _service.ChangeNotificationType(Context.User, type);
|
||||||
|
await Context.Channel.SendConfirmAsync("👌").ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Server { Server };
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[RequireUserPermission(GuildPermission.Administrator)]
|
||||||
|
public async Task XpExclude(Server _)
|
||||||
|
{
|
||||||
|
var ex = _service.ToggleExcludeServer(Context.Guild.Id);
|
||||||
|
|
||||||
|
await ReplyConfirmLocalized((ex ? "excluded" : "not_excluded"), Format.Bold(Context.Guild.ToString())).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Role { Role };
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[RequireUserPermission(GuildPermission.ManageRoles)]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task XpExclude(Role _, [Remainder] IRole role)
|
||||||
|
{
|
||||||
|
var ex = _service.ToggleExcludeRole(Context.Guild.Id, role.Id);
|
||||||
|
|
||||||
|
await ReplyConfirmLocalized((ex ? "excluded" : "not_excluded"), Format.Bold(role.ToString())).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Channel { Channel };
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[RequireUserPermission(GuildPermission.ManageChannels)]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task XpExclude(Channel _, [Remainder] ITextChannel channel)
|
||||||
|
{
|
||||||
|
var ex = _service.ToggleExcludeChannel(Context.Guild.Id, channel.Id);
|
||||||
|
|
||||||
|
await ReplyConfirmLocalized((ex ? "excluded" : "not_excluded"), Format.Bold(channel.ToString())).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task XpExclusionList()
|
||||||
|
{
|
||||||
|
var serverExcluded = _service.IsServerExcluded(Context.Guild.Id);
|
||||||
|
var roles = _service.GetExcludedRoles(Context.Guild.Id)
|
||||||
|
.Select(x => Context.Guild.GetRole(x)?.Name)
|
||||||
|
.Where(x => x != null);
|
||||||
|
|
||||||
|
var chans = (await Task.WhenAll(_service.GetExcludedChannels(Context.Guild.Id)
|
||||||
|
.Select(x => Context.Guild.GetChannelAsync(x)))
|
||||||
|
.ConfigureAwait(false))
|
||||||
|
.Where(x => x != null)
|
||||||
|
.Select(x => x.Name);
|
||||||
|
|
||||||
|
var embed = new EmbedBuilder()
|
||||||
|
.WithTitle(GetText("exclusion_list"))
|
||||||
|
.WithDescription((serverExcluded ? GetText("server_is_excluded") : GetText("server_is_not_excluded")))
|
||||||
|
.AddField(GetText("excluded_roles"), roles.Any() ? string.Join("\n", roles) : "-", false)
|
||||||
|
.AddField(GetText("excluded_channels"), chans.Any() ? string.Join("\n", chans) : "-", false)
|
||||||
|
.WithOkColor();
|
||||||
|
|
||||||
|
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public Task XpLeaderboard(int page = 1)
|
||||||
|
{
|
||||||
|
if (--page < 0)
|
||||||
|
return Task.CompletedTask;
|
||||||
|
|
||||||
|
return Context.Channel.SendPaginatedConfirmAsync(_client, page, async (curPage) =>
|
||||||
|
{
|
||||||
|
var users = _service.GetUserXps(Context.Guild.Id, curPage);
|
||||||
|
|
||||||
|
var embed = new EmbedBuilder()
|
||||||
|
.WithTitle(GetText("server_leaderboard"))
|
||||||
|
.WithOkColor();
|
||||||
|
|
||||||
|
if (!users.Any())
|
||||||
|
return embed.WithDescription("-");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < users.Length; i++)
|
||||||
|
{
|
||||||
|
var levelStats = LevelStats.FromXp(users[i].Xp + users[i].AwardedXp);
|
||||||
|
var user = await Context.Guild.GetUserAsync(users[i].UserId).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var userXpData = users[i];
|
||||||
|
|
||||||
|
var awardStr = "";
|
||||||
|
if (userXpData.AwardedXp > 0)
|
||||||
|
awardStr = $"(+{userXpData.AwardedXp})";
|
||||||
|
else if (userXpData.AwardedXp < 0)
|
||||||
|
awardStr = $"({userXpData.AwardedXp.ToString()})";
|
||||||
|
|
||||||
|
embed.AddField(
|
||||||
|
$"#{(i + 1 + curPage * 9)} {(user?.ToString() ?? users[i].UserId.ToString())}",
|
||||||
|
$"{GetText("level_x", levelStats.Level)} - {levelStats.TotalXp}xp {awardStr}");
|
||||||
|
}
|
||||||
|
return embed;
|
||||||
|
}
|
||||||
|
}, addPaginatedFooter: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task XpGlobalLeaderboard(int page = 1)
|
||||||
|
{
|
||||||
|
if (--page < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
await Context.Channel.SendPaginatedConfirmAsync(_client, page, async (curPage) =>
|
||||||
|
{
|
||||||
|
var users = _service.GetUserXps(curPage);
|
||||||
|
|
||||||
|
var embed = new EmbedBuilder()
|
||||||
|
.WithTitle(GetText("global_leaderboard"))
|
||||||
|
.WithOkColor();
|
||||||
|
|
||||||
|
if (!users.Any())
|
||||||
|
return embed.WithDescription("-");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < users.Length; i++)
|
||||||
|
{
|
||||||
|
var user = await Context.Guild.GetUserAsync(users[i].UserId).ConfigureAwait(false);
|
||||||
|
embed.AddField(
|
||||||
|
$"#{(i + 1 + curPage * 9)} {(user?.ToString() ?? users[i].UserId.ToString())}",
|
||||||
|
$"{GetText("level_x", LevelStats.FromXp(users[i].TotalXp).Level)} - {users[i].TotalXp}xp");
|
||||||
|
}
|
||||||
|
|
||||||
|
return embed;
|
||||||
|
}
|
||||||
|
}, addPaginatedFooter: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[RequireUserPermission(GuildPermission.Administrator)]
|
||||||
|
public async Task XpAdd(int amount, [Remainder] IGuildUser user)
|
||||||
|
{
|
||||||
|
if (amount == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_service.AddXp(user.Id, Context.Guild.Id, amount);
|
||||||
|
|
||||||
|
await ReplyConfirmLocalized("modified", Format.Bold(user.ToString()), Format.Bold(amount.ToString())).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -58,14 +58,14 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AngleSharp" Version="0.9.9" />
|
<PackageReference Include="AngleSharp" Version="0.9.9" />
|
||||||
<PackageReference Include="Discord.Net" Version="1.0.2-build-00797" />
|
<PackageReference Include="Discord.Net" Version="2.0.0-alpha-build-00824" />
|
||||||
<PackageReference Include="libvideo" Version="1.0.1" />
|
<PackageReference Include="libvideo" Version="1.0.1" />
|
||||||
<PackageReference Include="CoreCLR-NCalc" Version="2.1.2" />
|
<PackageReference Include="CoreCLR-NCalc" Version="2.1.2" />
|
||||||
<PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.19.0.138" />
|
<PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.19.0.138" />
|
||||||
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.20.0.701" />
|
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.20.0.701" />
|
||||||
<PackageReference Include="Google.Apis.Customsearch.v1" Version="1.20.0.466" />
|
<PackageReference Include="Google.Apis.Customsearch.v1" Version="1.20.0.466" />
|
||||||
<PackageReference Include="ImageSharp" Version="1.0.0-alpha9-00173" />
|
<PackageReference Include="ImageSharp" Version="1.0.0-alpha9-00194" />
|
||||||
<PackageReference Include="ImageSharp.Drawing" Version="1.0.0-alpha9-00168" />
|
<PackageReference Include="ImageSharp.Drawing" Version="1.0.0-alpha9-00189" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="1.1.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="1.1.1" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.1" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="1.1.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="1.1.1" />
|
||||||
@ -87,8 +87,22 @@
|
|||||||
<NoWarn>$(NoWarn);CS1573;CS1591</NoWarn>
|
<NoWarn>$(NoWarn);CS1573;CS1591</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" />
|
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" />
|
||||||
<DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="1.0.0" />
|
<DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="1.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Update="Resources\CommandStrings.Designer.cs">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -3573,4 +3573,211 @@
|
|||||||
<data name="nsfwtagblacklist_desc" xml:space="preserve">
|
<data name="nsfwtagblacklist_desc" xml:space="preserve">
|
||||||
<value>Toggles whether the tag is blacklisted or not in nsfw searches. Provide no parameters to see the list of blacklisted tags.</value>
|
<value>Toggles whether the tag is blacklisted or not in nsfw searches. Provide no parameters to see the list of blacklisted tags.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="experience_cmd" xml:space="preserve">
|
||||||
|
<value>experience xp</value>
|
||||||
|
</data>
|
||||||
|
<data name="experience_usage" xml:space="preserve">
|
||||||
|
<value>`{0}xp`</value>
|
||||||
|
</data>
|
||||||
|
<data name="experience_desc" xml:space="preserve">
|
||||||
|
<value>Shows your xp stats. Specify the user to show that user's stats instead.</value>
|
||||||
|
</data>
|
||||||
|
<data name="xpexclusionlist_cmd" xml:space="preserve">
|
||||||
|
<value>xpexclusionlist xpexl</value>
|
||||||
|
</data>
|
||||||
|
<data name="xpexclusionlist_usage" xml:space="preserve">
|
||||||
|
<value>`{0}xpexl`</value>
|
||||||
|
</data>
|
||||||
|
<data name="xpexclusionlist_desc" xml:space="preserve">
|
||||||
|
<value>Shows the roles and channels excluded from the XP system on this server, as well as whether the whole server is excluded.</value>
|
||||||
|
</data>
|
||||||
|
<data name="xpexclude_cmd" xml:space="preserve">
|
||||||
|
<value>xpexclude xpex</value>
|
||||||
|
</data>
|
||||||
|
<data name="xpexclude_usage" xml:space="preserve">
|
||||||
|
<value>`{0}xpex User @b1nzy` `{0}xpex Server`</value>
|
||||||
|
</data>
|
||||||
|
<data name="xpexclude_desc" xml:space="preserve">
|
||||||
|
<value>Exclude a user or a role from the xp system, or whole current server.</value>
|
||||||
|
</data>
|
||||||
|
<data name="xpnotify_cmd" xml:space="preserve">
|
||||||
|
<value>xpnotify xpn</value>
|
||||||
|
</data>
|
||||||
|
<data name="xpnotify_usage" xml:space="preserve">
|
||||||
|
<value>`{0}xpn global dm` `{0}xpn server channel`</value>
|
||||||
|
</data>
|
||||||
|
<data name="xpnotify_desc" xml:space="preserve">
|
||||||
|
<value>Sets how the bot should notify you when you get a `server` or `global` level. You can set `dm` (for the bot to send a direct message), `channel` (to get notified in the channel you sent the last message in) or `none` to disable.</value>
|
||||||
|
</data>
|
||||||
|
<data name="xprolerewards_cmd" xml:space="preserve">
|
||||||
|
<value>xprolerewards xprrs</value>
|
||||||
|
</data>
|
||||||
|
<data name="xprolerewards_usage" xml:space="preserve">
|
||||||
|
<value>`{0}xprrs`</value>
|
||||||
|
</data>
|
||||||
|
<data name="xprolerewards_desc" xml:space="preserve">
|
||||||
|
<value>Shows currently set role rewards.</value>
|
||||||
|
</data>
|
||||||
|
<data name="xprolereward_cmd" xml:space="preserve">
|
||||||
|
<value>xprolereward xprr</value>
|
||||||
|
</data>
|
||||||
|
<data name="xprolereward_usage" xml:space="preserve">
|
||||||
|
<value>`{0}xprr 3 Social`</value>
|
||||||
|
</data>
|
||||||
|
<data name="xprolereward_desc" xml:space="preserve">
|
||||||
|
<value>Sets a role reward on a specified level.</value>
|
||||||
|
</data>
|
||||||
|
<data name="xpleaderboard_cmd" xml:space="preserve">
|
||||||
|
<value>xpleaderboard xplb</value>
|
||||||
|
</data>
|
||||||
|
<data name="xpleaderboard_usage" xml:space="preserve">
|
||||||
|
<value>`{0}xplb`</value>
|
||||||
|
</data>
|
||||||
|
<data name="xpleaderboard_desc" xml:space="preserve">
|
||||||
|
<value>Shows current server's xp leaderboard.</value>
|
||||||
|
</data>
|
||||||
|
<data name="xpgloballeaderboard_cmd" xml:space="preserve">
|
||||||
|
<value>xpgleaderboard xpglb</value>
|
||||||
|
</data>
|
||||||
|
<data name="xpgloballeaderboard_usage" xml:space="preserve">
|
||||||
|
<value>`{0}xpglb`</value>
|
||||||
|
</data>
|
||||||
|
<data name="xpgloballeaderboard_desc" xml:space="preserve">
|
||||||
|
<value>Shows current server's xp leaderboard.</value>
|
||||||
|
</data>
|
||||||
|
<data name="xpadd_cmd" xml:space="preserve">
|
||||||
|
<value>xpadd</value>
|
||||||
|
</data>
|
||||||
|
<data name="xpadd_usage" xml:space="preserve">
|
||||||
|
<value>`{0}xpadd 100 @b1nzy`</value>
|
||||||
|
</data>
|
||||||
|
<data name="xpadd_desc" xml:space="preserve">
|
||||||
|
<value>Adds xp to a user on the server. This does not affect their global ranking. You can use negative values.</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubcreate_cmd" xml:space="preserve">
|
||||||
|
<value>clubcreate</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubcreate_usage" xml:space="preserve">
|
||||||
|
<value>`{0}clubcreate b1nzy's friends`</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubcreate_desc" xml:space="preserve">
|
||||||
|
<value>Creates a club. You must be atleast level 5 and not be in the club already.</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubinformation_cmd" xml:space="preserve">
|
||||||
|
<value>clubinfo</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubinformation_usage" xml:space="preserve">
|
||||||
|
<value>`{0}clubinfo b1nzy's friends#123`</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubinformation_desc" xml:space="preserve">
|
||||||
|
<value>Shows information about the club.</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubapply_cmd" xml:space="preserve">
|
||||||
|
<value>clubapply</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubapply_usage" xml:space="preserve">
|
||||||
|
<value>`{0}clubapply b1nzy's friends#123`</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubapply_desc" xml:space="preserve">
|
||||||
|
<value>Apply to join a club. You must meet that club's minimum level requirement, and not be on its ban list.</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubaccept_cmd" xml:space="preserve">
|
||||||
|
<value>clubaccept</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubaccept_usage" xml:space="preserve">
|
||||||
|
<value>`{0}clubaccept b1nzy#1337`</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubaccept_desc" xml:space="preserve">
|
||||||
|
<value>Apply to join a club. You must meet that club's minimum level requirement, and not be on its ban list.</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubleave_cmd" xml:space="preserve">
|
||||||
|
<value>clubleave</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubleave_usage" xml:space="preserve">
|
||||||
|
<value>`{0}clubleave`</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubleave_desc" xml:space="preserve">
|
||||||
|
<value>Leaves the club you're currently in.</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubdisband_cmd" xml:space="preserve">
|
||||||
|
<value>clubdisband</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubdisband_usage" xml:space="preserve">
|
||||||
|
<value>`{0}clubdisband`</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubdisband_desc" xml:space="preserve">
|
||||||
|
<value>Disbands the club you're the owner of. This action is irreversible.</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubkick_cmd" xml:space="preserve">
|
||||||
|
<value>clubkick</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubkick_usage" xml:space="preserve">
|
||||||
|
<value>`{0}clubkick b1nzy#1337`</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubkick_desc" xml:space="preserve">
|
||||||
|
<value>Kicks the user from the club. You must be the club owner. They will be able to apply again.</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubban_cmd" xml:space="preserve">
|
||||||
|
<value>clubban</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubban_usage" xml:space="preserve">
|
||||||
|
<value>`{0}clubban b1nzy#1337`</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubban_desc" xml:space="preserve">
|
||||||
|
<value>Bans the user from the club. You must be the club owner. They will not be able to apply again.</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubunban_cmd" xml:space="preserve">
|
||||||
|
<value>clubunban</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubunban_usage" xml:space="preserve">
|
||||||
|
<value>`{0}clubunban b1nzy#1337`</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubunban_desc" xml:space="preserve">
|
||||||
|
<value>Unbans the previously banned user from the club. You must be the club owner.</value>
|
||||||
|
</data>
|
||||||
|
<data name="clublevelreq_cmd" xml:space="preserve">
|
||||||
|
<value>clublevelreq</value>
|
||||||
|
</data>
|
||||||
|
<data name="clublevelreq_usage" xml:space="preserve">
|
||||||
|
<value>`{0}clublevelreq 7`</value>
|
||||||
|
</data>
|
||||||
|
<data name="clublevelreq_desc" xml:space="preserve">
|
||||||
|
<value>Sets the club required level to apply to join the club. You must be club owner. You can't set this number below 5.</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubicon_cmd" xml:space="preserve">
|
||||||
|
<value>clubicon</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubicon_usage" xml:space="preserve">
|
||||||
|
<value>`{0}clubicon https://i.imgur.com/htfDMfU.png`</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubicon_desc" xml:space="preserve">
|
||||||
|
<value>Sets the club icon.</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubapps_cmd" xml:space="preserve">
|
||||||
|
<value>clubapps</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubapps_usage" xml:space="preserve">
|
||||||
|
<value>`{0}clubapps 2`</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubapps_desc" xml:space="preserve">
|
||||||
|
<value>Shows the list of users who have applied to your club. Paginated. You must be club owner to use this command.</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubbans_cmd" xml:space="preserve">
|
||||||
|
<value>clubbans</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubbans_usage" xml:space="preserve">
|
||||||
|
<value>`{0}clubbans 2`</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubbans_desc" xml:space="preserve">
|
||||||
|
<value>Shows the list of users who have banned from your club. Paginated. You must be club owner to use this command.</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubleaderboard_cmd" xml:space="preserve">
|
||||||
|
<value>clublb</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubleaderboard_usage" xml:space="preserve">
|
||||||
|
<value>`{0}clublb 2`</value>
|
||||||
|
</data>
|
||||||
|
<data name="clubleaderboard_desc" xml:space="preserve">
|
||||||
|
<value>Shows club rankings on the specified page.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
@ -39,7 +39,7 @@ namespace NadekoBot.Services
|
|||||||
public string DefaultPrefix { get; private set; }
|
public string DefaultPrefix { get; private set; }
|
||||||
private ConcurrentDictionary<ulong, string> _prefixes { get; } = new ConcurrentDictionary<ulong, string>();
|
private ConcurrentDictionary<ulong, string> _prefixes { get; } = new ConcurrentDictionary<ulong, string>();
|
||||||
|
|
||||||
private ImmutableArray<AsyncLazy<IDMChannel>> ownerChannels { get; set; } = new ImmutableArray<AsyncLazy<IDMChannel>>();
|
private ImmutableArray<AsyncLazy<IDMChannel>> OwnerChannels { get; set; } = new ImmutableArray<AsyncLazy<IDMChannel>>();
|
||||||
|
|
||||||
public event Func<IUserMessage, CommandInfo, Task> CommandExecuted = delegate { return Task.CompletedTask; };
|
public event Func<IUserMessage, CommandInfo, Task> CommandExecuted = delegate { return Task.CompletedTask; };
|
||||||
public event Func<CommandInfo, ITextChannel, string, Task> CommandErrored = delegate { return Task.CompletedTask; };
|
public event Func<CommandInfo, ITextChannel, string, Task> CommandErrored = delegate { return Task.CompletedTask; };
|
||||||
|
@ -24,6 +24,8 @@ namespace NadekoBot.Services.Database
|
|||||||
IWaifuRepository Waifus { get; }
|
IWaifuRepository Waifus { get; }
|
||||||
IDiscordUserRepository DiscordUsers { get; }
|
IDiscordUserRepository DiscordUsers { get; }
|
||||||
IWarningsRepository Warnings { get; }
|
IWarningsRepository Warnings { get; }
|
||||||
|
IXpRepository Xp { get; }
|
||||||
|
IClubRepository Clubs { get; }
|
||||||
|
|
||||||
int Complete();
|
int Complete();
|
||||||
Task<int> CompleteAsync();
|
Task<int> CompleteAsync();
|
||||||
|
@ -68,6 +68,8 @@ Nadeko Support Server: https://discord.gg/nadekobot";
|
|||||||
public int PermissionVersion { get; set; }
|
public int PermissionVersion { get; set; }
|
||||||
public string DefaultPrefix { get; set; } = ".";
|
public string DefaultPrefix { get; set; } = ".";
|
||||||
public bool CustomReactionsStartWith { get; set; } = false;
|
public bool CustomReactionsStartWith { get; set; } = false;
|
||||||
|
public int XpPerMessage { get; set; } = 3;
|
||||||
|
public int XpMinutesTimeout { get; set; } = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BlockedCmdOrMdl : DbEntity
|
public class BlockedCmdOrMdl : DbEntity
|
||||||
|
47
src/NadekoBot/Services/Database/Models/ClubInfo.cs
Normal file
47
src/NadekoBot/Services/Database/Models/ClubInfo.cs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace NadekoBot.Services.Database.Models
|
||||||
|
{
|
||||||
|
public class ClubInfo : DbEntity
|
||||||
|
{
|
||||||
|
[MaxLength(20)]
|
||||||
|
public string Name { get; set; }
|
||||||
|
public int Discrim { get; set; }
|
||||||
|
|
||||||
|
public string ImageUrl { get; set; } = "";
|
||||||
|
public int MinimumLevelReq { get; set; } = 5;
|
||||||
|
public int Xp { get; set; } = 0;
|
||||||
|
|
||||||
|
public int OwnerId { get; set; }
|
||||||
|
public DiscordUser Owner { get; set; }
|
||||||
|
|
||||||
|
public List<DiscordUser> Users { get; set; } = new List<DiscordUser>();
|
||||||
|
|
||||||
|
public List<ClubApplicants> Applicants { get; set; } = new List<ClubApplicants>();
|
||||||
|
public List<ClubBans> Bans { get; set; } = new List<ClubBans>();
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Name + "#" + Discrim;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ClubApplicants
|
||||||
|
{
|
||||||
|
public int ClubId { get; set; }
|
||||||
|
public ClubInfo Club { get; set; }
|
||||||
|
|
||||||
|
public int UserId { get; set; }
|
||||||
|
public DiscordUser User { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ClubBans
|
||||||
|
{
|
||||||
|
public int ClubId { get; set; }
|
||||||
|
public ClubInfo Club { get; set; }
|
||||||
|
|
||||||
|
public int UserId { get; set; }
|
||||||
|
public DiscordUser User { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,6 @@
|
|||||||
namespace NadekoBot.Services.Database.Models
|
using System;
|
||||||
|
|
||||||
|
namespace NadekoBot.Services.Database.Models
|
||||||
{
|
{
|
||||||
public class DiscordUser : DbEntity
|
public class DiscordUser : DbEntity
|
||||||
{
|
{
|
||||||
@ -6,6 +8,22 @@
|
|||||||
public string Username { get; set; }
|
public string Username { get; set; }
|
||||||
public string Discriminator { get; set; }
|
public string Discriminator { get; set; }
|
||||||
public string AvatarId { get; set; }
|
public string AvatarId { get; set; }
|
||||||
|
|
||||||
|
public ClubInfo Club { get; set; }
|
||||||
|
public DateTime LastLevelUp { get; set; } = DateTime.UtcNow;
|
||||||
|
public XpNotificationType NotifyOnLevelUp { get; set; }
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is DiscordUser du
|
||||||
|
? du.UserId == UserId
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return UserId.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
public override string ToString() =>
|
public override string ToString() =>
|
||||||
Username + "#" + Discriminator;
|
Username + "#" + Discriminator;
|
||||||
|
@ -86,6 +86,8 @@ namespace NadekoBot.Services.Database.Models
|
|||||||
|
|
||||||
public StreamRoleSettings StreamRole { get; set; }
|
public StreamRoleSettings StreamRole { get; set; }
|
||||||
|
|
||||||
|
public XpSettings XpSettings { get; set; }
|
||||||
|
|
||||||
//public List<ProtectionIgnoredChannel> ProtectionIgnoredChannels { get; set; } = new List<ProtectionIgnoredChannel>();
|
//public List<ProtectionIgnoredChannel> ProtectionIgnoredChannels { get; set; } = new List<ProtectionIgnoredChannel>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
16
src/NadekoBot/Services/Database/Models/UserXpStats.cs
Normal file
16
src/NadekoBot/Services/Database/Models/UserXpStats.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NadekoBot.Services.Database.Models
|
||||||
|
{
|
||||||
|
public class UserXpStats : DbEntity
|
||||||
|
{
|
||||||
|
public ulong UserId { get; set; }
|
||||||
|
public ulong GuildId { get; set; }
|
||||||
|
public int Xp { get; set; }
|
||||||
|
public int AwardedXp { get; set; }
|
||||||
|
public XpNotificationType NotifyOnLevelUp { get; set; }
|
||||||
|
public DateTime LastLevelUp { get; set; } = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum XpNotificationType { None, Dm, Channel }
|
||||||
|
}
|
50
src/NadekoBot/Services/Database/Models/XpSettings.cs
Normal file
50
src/NadekoBot/Services/Database/Models/XpSettings.cs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace NadekoBot.Services.Database.Models
|
||||||
|
{
|
||||||
|
public class XpSettings : DbEntity
|
||||||
|
{
|
||||||
|
public int GuildConfigId { get; set; }
|
||||||
|
public GuildConfig GuildConfig { get; set; }
|
||||||
|
|
||||||
|
public HashSet<XpRoleReward> RoleRewards { get; set; } = new HashSet<XpRoleReward>();
|
||||||
|
public bool XpRoleRewardExclusive { get; set; }
|
||||||
|
public string NotifyMessage { get; set; } = "Congratulations {0}! You have reached level {1}!";
|
||||||
|
public HashSet<ExcludedItem> ExclusionList { get; set; } = new HashSet<ExcludedItem>();
|
||||||
|
public bool ServerExcluded { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ExcludedItemType { Channel, Role }
|
||||||
|
|
||||||
|
public class XpRoleReward : DbEntity
|
||||||
|
{
|
||||||
|
public int Level { get; set; }
|
||||||
|
public ulong RoleId { get; set; }
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return Level.GetHashCode() ^ RoleId.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is XpRoleReward xrr && xrr.Level == Level && xrr.RoleId == RoleId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ExcludedItem : DbEntity
|
||||||
|
{
|
||||||
|
public ulong ItemId { get; set; }
|
||||||
|
public ExcludedItemType ItemType { get; set; }
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return ItemId.GetHashCode() ^ ItemType.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is ExcludedItem ei && ei.ItemId == ItemId && ei.ItemType == ItemType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ using System.Linq;
|
|||||||
using NadekoBot.Services.Database.Models;
|
using NadekoBot.Services.Database.Models;
|
||||||
using NadekoBot.Extensions;
|
using NadekoBot.Extensions;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace NadekoBot.Services.Database
|
namespace NadekoBot.Services.Database
|
||||||
{
|
{
|
||||||
@ -43,6 +44,8 @@ namespace NadekoBot.Services.Database
|
|||||||
public DbSet<UserPokeTypes> PokeGame { get; set; }
|
public DbSet<UserPokeTypes> PokeGame { get; set; }
|
||||||
public DbSet<WaifuUpdate> WaifuUpdates { get; set; }
|
public DbSet<WaifuUpdate> WaifuUpdates { get; set; }
|
||||||
public DbSet<Warning> Warnings { get; set; }
|
public DbSet<Warning> Warnings { get; set; }
|
||||||
|
public DbSet<UserXpStats> UserXpStats { get; set; }
|
||||||
|
public DbSet<ClubInfo> Clubs { get; set; }
|
||||||
|
|
||||||
//logging
|
//logging
|
||||||
public DbSet<LogSetting> LogSettings { get; set; }
|
public DbSet<LogSetting> LogSettings { get; set; }
|
||||||
@ -70,24 +73,6 @@ namespace NadekoBot.Services.Database
|
|||||||
{
|
{
|
||||||
var bc = new BotConfig();
|
var bc = new BotConfig();
|
||||||
|
|
||||||
bc.ModulePrefixes.AddRange(new HashSet<ModulePrefix>()
|
|
||||||
{
|
|
||||||
new ModulePrefix() { ModuleName = "Administration", Prefix = "." },
|
|
||||||
new ModulePrefix() { ModuleName = "Searches", Prefix = "~" },
|
|
||||||
new ModulePrefix() { ModuleName = "Translator", Prefix = "~" },
|
|
||||||
new ModulePrefix() { ModuleName = "NSFW", Prefix = "~" },
|
|
||||||
new ModulePrefix() { ModuleName = "ClashOfClans", Prefix = "," },
|
|
||||||
new ModulePrefix() { ModuleName = "Help", Prefix = "-" },
|
|
||||||
new ModulePrefix() { ModuleName = "Music", Prefix = "!!" },
|
|
||||||
new ModulePrefix() { ModuleName = "Trello", Prefix = "trello" },
|
|
||||||
new ModulePrefix() { ModuleName = "Games", Prefix = ">" },
|
|
||||||
new ModulePrefix() { ModuleName = "Gambling", Prefix = "$" },
|
|
||||||
new ModulePrefix() { ModuleName = "Permissions", Prefix = ";" },
|
|
||||||
new ModulePrefix() { ModuleName = "Pokemon", Prefix = ">" },
|
|
||||||
new ModulePrefix() { ModuleName = "Utility", Prefix = "." },
|
|
||||||
new ModulePrefix() { ModuleName = "CustomReactions", Prefix = "." },
|
|
||||||
new ModulePrefix() { ModuleName = "PokeGame", Prefix = ">" }
|
|
||||||
});
|
|
||||||
bc.RaceAnimals.AddRange(new HashSet<RaceAnimal>
|
bc.RaceAnimals.AddRange(new HashSet<RaceAnimal>
|
||||||
{
|
{
|
||||||
new RaceAnimal { Icon = "🐼", Name = "Panda" },
|
new RaceAnimal { Icon = "🐼", Name = "Panda" },
|
||||||
@ -176,7 +161,14 @@ namespace NadekoBot.Services.Database
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region BotConfig
|
#region BotConfig
|
||||||
//var botConfigEntity = modelBuilder.Entity<BotConfig>();
|
var botConfigEntity = modelBuilder.Entity<BotConfig>();
|
||||||
|
|
||||||
|
botConfigEntity.Property(x => x.XpMinutesTimeout)
|
||||||
|
.HasDefaultValue(5);
|
||||||
|
|
||||||
|
botConfigEntity.Property(x => x.XpPerMessage)
|
||||||
|
.HasDefaultValue(3);
|
||||||
|
|
||||||
//botConfigEntity
|
//botConfigEntity
|
||||||
// .HasMany(c => c.ModulePrefixes)
|
// .HasMany(c => c.ModulePrefixes)
|
||||||
// .WithOne(mp => mp.BotConfig)
|
// .WithOne(mp => mp.BotConfig)
|
||||||
@ -277,9 +269,19 @@ namespace NadekoBot.Services.Database
|
|||||||
// .WithOne();
|
// .WithOne();
|
||||||
// //.HasForeignKey<WaifuInfo>(w => w.ClaimerId)
|
// //.HasForeignKey<WaifuInfo>(w => w.ClaimerId)
|
||||||
// //.IsRequired(false);
|
// //.IsRequired(false);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region DiscordUser
|
||||||
|
|
||||||
var du = modelBuilder.Entity<DiscordUser>();
|
var du = modelBuilder.Entity<DiscordUser>();
|
||||||
du.HasAlternateKey(w => w.UserId);
|
du.HasAlternateKey(w => w.UserId);
|
||||||
|
du.HasOne(x => x.Club)
|
||||||
|
.WithMany(x => x.Users)
|
||||||
|
.IsRequired(false);
|
||||||
|
|
||||||
|
modelBuilder.Entity<DiscordUser>()
|
||||||
|
.Property(x => x.LastLevelUp)
|
||||||
|
.HasDefaultValue(DateTime.Now);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -292,6 +294,63 @@ namespace NadekoBot.Services.Database
|
|||||||
pr.HasIndex(x => x.UserId)
|
pr.HasIndex(x => x.UserId)
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region XpStats
|
||||||
|
modelBuilder.Entity<UserXpStats>()
|
||||||
|
.HasIndex(x => new { x.UserId, x.GuildId })
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
modelBuilder.Entity<UserXpStats>()
|
||||||
|
.Property(x => x.LastLevelUp)
|
||||||
|
.HasDefaultValue(DateTime.Now);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region XpSettings
|
||||||
|
modelBuilder.Entity<XpSettings>()
|
||||||
|
.HasOne(x => x.GuildConfig)
|
||||||
|
.WithOne(x => x.XpSettings);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region XpRoleReward
|
||||||
|
modelBuilder.Entity<XpRoleReward>()
|
||||||
|
.HasAlternateKey(x => x.Level);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Club
|
||||||
|
var ci = modelBuilder.Entity<ClubInfo>();
|
||||||
|
ci.HasOne(x => x.Owner)
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey<ClubInfo>(x => x.OwnerId);
|
||||||
|
|
||||||
|
|
||||||
|
ci.HasAlternateKey(x => new { x.Name, x.Discrim });
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ClubManytoMany
|
||||||
|
|
||||||
|
modelBuilder.Entity<ClubApplicants>()
|
||||||
|
.HasKey(t => new { t.ClubId, t.UserId });
|
||||||
|
|
||||||
|
modelBuilder.Entity<ClubApplicants>()
|
||||||
|
.HasOne(pt => pt.User)
|
||||||
|
.WithMany();
|
||||||
|
|
||||||
|
modelBuilder.Entity<ClubApplicants>()
|
||||||
|
.HasOne(pt => pt.Club)
|
||||||
|
.WithMany(x => x.Applicants);
|
||||||
|
|
||||||
|
modelBuilder.Entity<ClubBans>()
|
||||||
|
.HasKey(t => new { t.ClubId, t.UserId });
|
||||||
|
|
||||||
|
modelBuilder.Entity<ClubBans>()
|
||||||
|
.HasOne(pt => pt.User)
|
||||||
|
.WithMany();
|
||||||
|
|
||||||
|
modelBuilder.Entity<ClubBans>()
|
||||||
|
.HasOne(pt => pt.Club)
|
||||||
|
.WithMany(x => x.Bans);
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using NadekoBot.Services.Database.Models;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace NadekoBot.Services.Database.Repositories
|
||||||
|
{
|
||||||
|
public interface IClubRepository : IRepository<ClubInfo>
|
||||||
|
{
|
||||||
|
int GetNextDiscrim(string clubName);
|
||||||
|
ClubInfo GetByName(string v, int discrim, Func<DbSet<ClubInfo>, IQueryable<ClubInfo>> func = null);
|
||||||
|
ClubInfo GetByOwner(ulong userId, Func<DbSet<ClubInfo>, IQueryable<ClubInfo>> func = null);
|
||||||
|
ClubInfo GetByMember(ulong userId, Func<DbSet<ClubInfo>, IQueryable<ClubInfo>> func = null);
|
||||||
|
ClubInfo[] GetClubLeaderboardPage(int page);
|
||||||
|
}
|
||||||
|
}
|
@ -16,5 +16,6 @@ namespace NadekoBot.Services.Database.Repositories
|
|||||||
void SetCleverbotEnabled(ulong id, bool cleverbotEnabled);
|
void SetCleverbotEnabled(ulong id, bool cleverbotEnabled);
|
||||||
IEnumerable<GuildConfig> Permissionsv2ForAll(List<long> include);
|
IEnumerable<GuildConfig> Permissionsv2ForAll(List<long> include);
|
||||||
GuildConfig GcWithPermissionsv2For(ulong guildId);
|
GuildConfig GcWithPermissionsv2For(ulong guildId);
|
||||||
|
XpSettings XpSettingsFor(ulong guildId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
using NadekoBot.Services.Database.Models;
|
||||||
|
|
||||||
|
namespace NadekoBot.Services.Database.Repositories
|
||||||
|
{
|
||||||
|
public interface IXpRepository : IRepository<UserXpStats>
|
||||||
|
{
|
||||||
|
UserXpStats GetOrCreateUser(ulong guildId, ulong userId);
|
||||||
|
int GetTotalUserXp(ulong userId);
|
||||||
|
UserXpStats[] GetUsersFor(ulong guildId, int page);
|
||||||
|
(ulong UserId, int TotalXp)[] GetUsersFor(int page);
|
||||||
|
int GetUserGlobalRanking(ulong userId);
|
||||||
|
int GetUserGuildRanking(ulong userId, ulong guildId);
|
||||||
|
}
|
||||||
|
}
|
@ -20,7 +20,6 @@ namespace NadekoBot.Services.Database.Repositories.Impl
|
|||||||
.Include(bc => bc.RaceAnimals)
|
.Include(bc => bc.RaceAnimals)
|
||||||
.Include(bc => bc.Blacklist)
|
.Include(bc => bc.Blacklist)
|
||||||
.Include(bc => bc.EightBallResponses)
|
.Include(bc => bc.EightBallResponses)
|
||||||
.Include(bc => bc.ModulePrefixes)
|
|
||||||
.Include(bc => bc.StartupCommands)
|
.Include(bc => bc.StartupCommands)
|
||||||
.Include(bc => bc.BlockedCommands)
|
.Include(bc => bc.BlockedCommands)
|
||||||
.Include(bc => bc.BlockedModules)
|
.Include(bc => bc.BlockedModules)
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
using NadekoBot.Services.Database.Models;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NadekoBot.Services.Database.Repositories.Impl
|
||||||
|
{
|
||||||
|
public class ClubRepository : Repository<ClubInfo>, IClubRepository
|
||||||
|
{
|
||||||
|
public ClubRepository(DbContext context) : base(context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClubInfo GetByOwner(ulong userId, Func<DbSet<ClubInfo>, IQueryable<ClubInfo>> func = null)
|
||||||
|
{
|
||||||
|
if (func == null)
|
||||||
|
return _set
|
||||||
|
.Include(x => x.Bans)
|
||||||
|
.Include(x => x.Applicants)
|
||||||
|
.Include(x => x.Users)
|
||||||
|
.Include(x => x.Owner)
|
||||||
|
.FirstOrDefault(x => x.Owner.UserId == userId);
|
||||||
|
|
||||||
|
return func(_set).FirstOrDefault(x => x.Owner.UserId == userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClubInfo GetByName(string name, int discrim, Func<DbSet<ClubInfo>, IQueryable<ClubInfo>> func = null)
|
||||||
|
{
|
||||||
|
if (func == null)
|
||||||
|
return _set
|
||||||
|
.Include(x => x.Bans)
|
||||||
|
.Include(x => x.Applicants)
|
||||||
|
.Include(x => x.Users)
|
||||||
|
.FirstOrDefault(x => x.Name.ToLowerInvariant() == name && x.Discrim == discrim);
|
||||||
|
|
||||||
|
return func(_set).FirstOrDefault(x => x.Name == name && x.Discrim == discrim);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetNextDiscrim(string clubName)
|
||||||
|
{
|
||||||
|
return _set
|
||||||
|
.Where(x => x.Name.ToLowerInvariant() == clubName.ToLowerInvariant())
|
||||||
|
.Max(x => x.Discrim) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClubInfo GetByMember(ulong userId, Func<DbSet<ClubInfo>, IQueryable<ClubInfo>> func = null)
|
||||||
|
{
|
||||||
|
if (func == null)
|
||||||
|
return _set.Include(x => x.Users)
|
||||||
|
.Include(x => x.Bans)
|
||||||
|
.Include(x => x.Applicants)
|
||||||
|
.FirstOrDefault(x => x.Users.Any(y => y.UserId == userId));
|
||||||
|
|
||||||
|
return func(_set).FirstOrDefault(x => x.Users.Any(y => y.UserId == userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClubInfo[] GetClubLeaderboardPage(int page)
|
||||||
|
{
|
||||||
|
return _set.OrderBy(x => x.Xp)
|
||||||
|
.Skip(page * 9)
|
||||||
|
.Take(9)
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,7 +15,15 @@ namespace NadekoBot.Services.Database.Repositories.Impl
|
|||||||
{
|
{
|
||||||
DiscordUser toReturn;
|
DiscordUser toReturn;
|
||||||
|
|
||||||
toReturn = _set.FirstOrDefault(u => u.UserId == original.Id);
|
toReturn = _set.Include(x => x.Club)
|
||||||
|
.FirstOrDefault(u => u.UserId == original.Id);
|
||||||
|
|
||||||
|
if (toReturn != null)
|
||||||
|
{
|
||||||
|
toReturn.AvatarId = original.AvatarId;
|
||||||
|
toReturn.Username = original.Username;
|
||||||
|
toReturn.Discriminator = original.Discriminator;
|
||||||
|
}
|
||||||
|
|
||||||
if (toReturn == null)
|
if (toReturn == null)
|
||||||
_set.Add(toReturn = new DiscordUser()
|
_set.Add(toReturn = new DiscordUser()
|
||||||
@ -24,6 +32,7 @@ namespace NadekoBot.Services.Database.Repositories.Impl
|
|||||||
Discriminator = original.Discriminator,
|
Discriminator = original.Discriminator,
|
||||||
UserId = original.Id,
|
UserId = original.Id,
|
||||||
Username = original.Username,
|
Username = original.Username,
|
||||||
|
Club = null,
|
||||||
});
|
});
|
||||||
|
|
||||||
return toReturn;
|
return toReturn;
|
||||||
|
@ -189,5 +189,19 @@ namespace NadekoBot.Services.Database.Repositories.Impl
|
|||||||
|
|
||||||
conf.CleverbotEnabled = cleverbotEnabled;
|
conf.CleverbotEnabled = cleverbotEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public XpSettings XpSettingsFor(ulong guildId)
|
||||||
|
{
|
||||||
|
var gc = For(guildId,
|
||||||
|
set => set.Include(x => x.XpSettings)
|
||||||
|
.ThenInclude(x => x.RoleRewards)
|
||||||
|
.Include(x => x.XpSettings)
|
||||||
|
.ThenInclude(x => x.ExclusionList));
|
||||||
|
|
||||||
|
if (gc.XpSettings == null)
|
||||||
|
gc.XpSettings = new XpSettings();
|
||||||
|
|
||||||
|
return gc.XpSettings;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
using NadekoBot.Services.Database.Models;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
//todo add pagination to .lb
|
||||||
|
namespace NadekoBot.Services.Database.Repositories.Impl
|
||||||
|
{
|
||||||
|
public class XpRepository : Repository<UserXpStats>, IXpRepository
|
||||||
|
{
|
||||||
|
public XpRepository(DbContext context) : base(context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserXpStats GetOrCreateUser(ulong guildId, ulong userId)
|
||||||
|
{
|
||||||
|
var usr = _set.FirstOrDefault(x => x.UserId == userId && x.GuildId == guildId);
|
||||||
|
|
||||||
|
if (usr == null)
|
||||||
|
{
|
||||||
|
_context.Add(usr = new UserXpStats()
|
||||||
|
{
|
||||||
|
Xp = 0,
|
||||||
|
UserId = userId,
|
||||||
|
NotifyOnLevelUp = XpNotificationType.None,
|
||||||
|
GuildId = guildId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return usr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetTotalUserXp(ulong userId)
|
||||||
|
{
|
||||||
|
return _set.Where(x => x.UserId == userId).Sum(x => x.Xp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserXpStats[] GetUsersFor(ulong guildId, int page)
|
||||||
|
{
|
||||||
|
return _set.Where(x => x.GuildId == guildId)
|
||||||
|
.OrderByDescending(x => x.Xp + x.AwardedXp)
|
||||||
|
.Skip(page * 9)
|
||||||
|
.Take(9)
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetUserGlobalRanking(ulong userId)
|
||||||
|
{
|
||||||
|
return _set
|
||||||
|
.GroupBy(x => x.UserId)
|
||||||
|
.Count(x => x.Sum(y => y.Xp) > _set
|
||||||
|
.Where(y => y.UserId == userId)
|
||||||
|
.Sum(y => y.Xp)) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetUserGuildRanking(ulong userId, ulong guildId)
|
||||||
|
{
|
||||||
|
return _set
|
||||||
|
.Where(x => x.GuildId == guildId)
|
||||||
|
.Count(x => x.Xp > (_set
|
||||||
|
.Where(y => y.UserId == userId && y.GuildId == guildId)
|
||||||
|
.Sum(y => y.Xp))) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public (ulong UserId, int TotalXp)[] GetUsersFor(int page)
|
||||||
|
{
|
||||||
|
return (from orduser in _set
|
||||||
|
group orduser by orduser.UserId into g
|
||||||
|
orderby g.Sum(x => x.Xp) descending
|
||||||
|
select new { UserId = g.Key, TotalXp = g.Sum(x => x.Xp) })
|
||||||
|
.Skip(page * 9)
|
||||||
|
.Take(9)
|
||||||
|
.AsEnumerable()
|
||||||
|
.Select(x => (x.UserId, x.TotalXp))
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -57,6 +57,12 @@ namespace NadekoBot.Services.Database
|
|||||||
private IWarningsRepository _warnings;
|
private IWarningsRepository _warnings;
|
||||||
public IWarningsRepository Warnings => _warnings ?? (_warnings = new WarningsRepository(_context));
|
public IWarningsRepository Warnings => _warnings ?? (_warnings = new WarningsRepository(_context));
|
||||||
|
|
||||||
|
private IXpRepository _xp;
|
||||||
|
public IXpRepository Xp => _xp ?? (_xp = new XpRepository(_context));
|
||||||
|
|
||||||
|
private IClubRepository _clubs;
|
||||||
|
public IClubRepository Clubs => _clubs ?? (_clubs = new ClubRepository(_context));
|
||||||
|
|
||||||
public UnitOfWork(NadekoContext context)
|
public UnitOfWork(NadekoContext context)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using NadekoBot.Services.Database;
|
using NadekoBot.Services.Database;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace NadekoBot.Services
|
namespace NadekoBot.Services
|
||||||
{
|
{
|
||||||
public class DbService
|
public class DbService
|
||||||
{
|
{
|
||||||
private readonly DbContextOptions options;
|
private readonly DbContextOptions options;
|
||||||
|
private readonly DbContextOptions migrateOptions;
|
||||||
|
|
||||||
private readonly string _connectionString;
|
private readonly string _connectionString;
|
||||||
|
|
||||||
@ -15,25 +17,23 @@ namespace NadekoBot.Services
|
|||||||
var optionsBuilder = new DbContextOptionsBuilder();
|
var optionsBuilder = new DbContextOptionsBuilder();
|
||||||
optionsBuilder.UseSqlite(creds.Db.ConnectionString);
|
optionsBuilder.UseSqlite(creds.Db.ConnectionString);
|
||||||
options = optionsBuilder.Options;
|
options = optionsBuilder.Options;
|
||||||
//switch (_creds.Db.Type.ToUpperInvariant())
|
|
||||||
//{
|
|
||||||
// case "SQLITE":
|
|
||||||
// dbType = typeof(NadekoSqliteContext);
|
|
||||||
// break;
|
|
||||||
// //case "SQLSERVER":
|
|
||||||
// // dbType = typeof(NadekoSqlServerContext);
|
|
||||||
// // break;
|
|
||||||
// default:
|
|
||||||
// break;
|
|
||||||
|
|
||||||
//}
|
optionsBuilder = new DbContextOptionsBuilder();
|
||||||
|
optionsBuilder.UseSqlite(creds.Db.ConnectionString, x => x.SuppressForeignKeyEnforcement());
|
||||||
|
migrateOptions = optionsBuilder.Options;
|
||||||
}
|
}
|
||||||
|
|
||||||
public NadekoContext GetDbContext()
|
public NadekoContext GetDbContext()
|
||||||
{
|
{
|
||||||
var context = new NadekoContext(options);
|
var context = new NadekoContext(options);
|
||||||
|
if (context.Database.GetPendingMigrations().Any())
|
||||||
|
{
|
||||||
|
var mContext = new NadekoContext(migrateOptions);
|
||||||
|
mContext.Database.Migrate();
|
||||||
|
mContext.SaveChanges();
|
||||||
|
mContext.Dispose();
|
||||||
|
}
|
||||||
context.Database.SetCommandTimeout(60);
|
context.Database.SetCommandTimeout(60);
|
||||||
context.Database.Migrate();
|
|
||||||
context.EnsureSeedData();
|
context.EnsureSeedData();
|
||||||
|
|
||||||
//set important sqlite stuffs
|
//set important sqlite stuffs
|
||||||
|
@ -17,6 +17,8 @@ namespace NadekoBot.Services
|
|||||||
ImmutableArray<byte> WifeMatrix { get; }
|
ImmutableArray<byte> WifeMatrix { get; }
|
||||||
ImmutableArray<byte> RategirlDot { get; }
|
ImmutableArray<byte> RategirlDot { get; }
|
||||||
|
|
||||||
|
ImmutableArray<byte> XpCard { get; }
|
||||||
|
|
||||||
void Reload();
|
void Reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,6 +122,18 @@ namespace NadekoBot.Services.Impl
|
|||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
|
case BotConfigEditType.XpPerMessage:
|
||||||
|
if (int.TryParse(newValue, out var xp) && xp > 0)
|
||||||
|
bc.XpPerMessage = xp;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case BotConfigEditType.XpMinutesTimeout:
|
||||||
|
if (int.TryParse(newValue, out var min) && min > 0)
|
||||||
|
bc.XpMinutesTimeout = min;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -377,8 +377,7 @@ namespace NadekoBot.Services.Impl
|
|||||||
|
|
||||||
private string ConvertToLanguageCode(string language)
|
private string ConvertToLanguageCode(string language)
|
||||||
{
|
{
|
||||||
string mode;
|
_languageDictionary.TryGetValue(language, out var mode);
|
||||||
_languageDictionary.TryGetValue(language, out mode);
|
|
||||||
return mode;
|
return mode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@ namespace NadekoBot.Services.Impl
|
|||||||
private const string _wifeMatrixPath = _basePath + "rategirl/wifematrix.png";
|
private const string _wifeMatrixPath = _basePath + "rategirl/wifematrix.png";
|
||||||
private const string _rategirlDot = _basePath + "rategirl/dot.png";
|
private const string _rategirlDot = _basePath + "rategirl/dot.png";
|
||||||
|
|
||||||
|
private const string _xpCardPath = _basePath + "xp/xp.png";
|
||||||
|
|
||||||
|
|
||||||
public ImmutableArray<byte> Heads { get; private set; }
|
public ImmutableArray<byte> Heads { get; private set; }
|
||||||
public ImmutableArray<byte> Tails { get; private set; }
|
public ImmutableArray<byte> Tails { get; private set; }
|
||||||
@ -40,6 +42,8 @@ namespace NadekoBot.Services.Impl
|
|||||||
public ImmutableArray<byte> WifeMatrix { get; private set; }
|
public ImmutableArray<byte> WifeMatrix { get; private set; }
|
||||||
public ImmutableArray<byte> RategirlDot { get; private set; }
|
public ImmutableArray<byte> RategirlDot { get; private set; }
|
||||||
|
|
||||||
|
public ImmutableArray<byte> XpCard { get; private set; }
|
||||||
|
|
||||||
public ImagesService()
|
public ImagesService()
|
||||||
{
|
{
|
||||||
_log = LogManager.GetCurrentClassLogger();
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
@ -76,6 +80,8 @@ namespace NadekoBot.Services.Impl
|
|||||||
|
|
||||||
WifeMatrix = File.ReadAllBytes(_wifeMatrixPath).ToImmutableArray();
|
WifeMatrix = File.ReadAllBytes(_wifeMatrixPath).ToImmutableArray();
|
||||||
RategirlDot = File.ReadAllBytes(_rategirlDot).ToImmutableArray();
|
RategirlDot = File.ReadAllBytes(_rategirlDot).ToImmutableArray();
|
||||||
|
|
||||||
|
XpCard = File.ReadAllBytes(_xpCardPath).ToImmutableArray();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -20,7 +20,7 @@ namespace NadekoBot.Services.Impl
|
|||||||
private readonly IBotCredentials _creds;
|
private readonly IBotCredentials _creds;
|
||||||
private readonly DateTime _started;
|
private readonly DateTime _started;
|
||||||
|
|
||||||
public const string BotVersion = "1.7.1";
|
public const string BotVersion = "1.8";
|
||||||
|
|
||||||
public string Author => "Kwoth#2560";
|
public string Author => "Kwoth#2560";
|
||||||
public string Library => "Discord.Net";
|
public string Library => "Discord.Net";
|
||||||
|
@ -132,7 +132,7 @@ namespace NadekoBot.Extensions
|
|||||||
public static string ToJson<T>(this T any, Formatting formatting = Formatting.Indented) =>
|
public static string ToJson<T>(this T any, Formatting formatting = Formatting.Indented) =>
|
||||||
JsonConvert.SerializeObject(any, formatting);
|
JsonConvert.SerializeObject(any, formatting);
|
||||||
|
|
||||||
public static Stream ToStream(this ImageSharp.Image<Rgba32> img)
|
public static MemoryStream ToStream(this ImageSharp.Image<Rgba32> img)
|
||||||
{
|
{
|
||||||
var imageStream = new MemoryStream();
|
var imageStream = new MemoryStream();
|
||||||
img.SaveAsPng(imageStream);
|
img.SaveAsPng(imageStream);
|
||||||
|
@ -10,7 +10,7 @@ namespace NadekoBot.Extensions
|
|||||||
public static class IMessageChannelExtensions
|
public static class IMessageChannelExtensions
|
||||||
{
|
{
|
||||||
public static Task<IUserMessage> EmbedAsync(this IMessageChannel ch, EmbedBuilder embed, string msg = "")
|
public static Task<IUserMessage> EmbedAsync(this IMessageChannel ch, EmbedBuilder embed, string msg = "")
|
||||||
=> ch.SendMessageAsync(msg, embed: embed);
|
=> ch.SendMessageAsync(msg, embed: embed.Build());
|
||||||
|
|
||||||
public static Task<IUserMessage> SendErrorAsync(this IMessageChannel ch, string title, string error, string url = null, string footer = null)
|
public static Task<IUserMessage> SendErrorAsync(this IMessageChannel ch, string title, string error, string url = null, string footer = null)
|
||||||
{
|
{
|
||||||
@ -20,11 +20,11 @@ namespace NadekoBot.Extensions
|
|||||||
eb.WithUrl(url);
|
eb.WithUrl(url);
|
||||||
if (!string.IsNullOrWhiteSpace(footer))
|
if (!string.IsNullOrWhiteSpace(footer))
|
||||||
eb.WithFooter(efb => efb.WithText(footer));
|
eb.WithFooter(efb => efb.WithText(footer));
|
||||||
return ch.SendMessageAsync("", embed: eb);
|
return ch.SendMessageAsync("", embed: eb.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<IUserMessage> SendErrorAsync(this IMessageChannel ch, string error)
|
public static Task<IUserMessage> SendErrorAsync(this IMessageChannel ch, string error)
|
||||||
=> ch.SendMessageAsync("", embed: new EmbedBuilder().WithErrorColor().WithDescription(error));
|
=> ch.SendMessageAsync("", embed: new EmbedBuilder().WithErrorColor().WithDescription(error).Build());
|
||||||
|
|
||||||
public static Task<IUserMessage> SendConfirmAsync(this IMessageChannel ch, string title, string text, string url = null, string footer = null)
|
public static Task<IUserMessage> SendConfirmAsync(this IMessageChannel ch, string title, string text, string url = null, string footer = null)
|
||||||
{
|
{
|
||||||
@ -34,11 +34,11 @@ namespace NadekoBot.Extensions
|
|||||||
eb.WithUrl(url);
|
eb.WithUrl(url);
|
||||||
if (!string.IsNullOrWhiteSpace(footer))
|
if (!string.IsNullOrWhiteSpace(footer))
|
||||||
eb.WithFooter(efb => efb.WithText(footer));
|
eb.WithFooter(efb => efb.WithText(footer));
|
||||||
return ch.SendMessageAsync("", embed: eb);
|
return ch.SendMessageAsync("", embed: eb.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<IUserMessage> SendConfirmAsync(this IMessageChannel ch, string text)
|
public static Task<IUserMessage> SendConfirmAsync(this IMessageChannel ch, string text)
|
||||||
=> ch.SendMessageAsync("", embed: new EmbedBuilder().WithOkColor().WithDescription(text));
|
=> ch.SendMessageAsync("", embed: new EmbedBuilder().WithOkColor().WithDescription(text).Build());
|
||||||
|
|
||||||
public static Task<IUserMessage> SendTableAsync<T>(this IMessageChannel ch, string seed, IEnumerable<T> items, Func<T, string> howToPrint, int columns = 3)
|
public static Task<IUserMessage> SendTableAsync<T>(this IMessageChannel ch, string seed, IEnumerable<T> items, Func<T, string> howToPrint, int columns = 3)
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Discord;
|
using Discord;
|
||||||
|
using NadekoBot.Services.Database.Models;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -8,14 +9,14 @@ namespace NadekoBot.Extensions
|
|||||||
public static class IUserExtensions
|
public static class IUserExtensions
|
||||||
{
|
{
|
||||||
public static async Task<IUserMessage> SendConfirmAsync(this IUser user, string text)
|
public static async Task<IUserMessage> SendConfirmAsync(this IUser user, string text)
|
||||||
=> await (await user.GetOrCreateDMChannelAsync()).SendMessageAsync("", embed: new EmbedBuilder().WithOkColor().WithDescription(text));
|
=> await (await user.GetOrCreateDMChannelAsync()).SendMessageAsync("", embed: new EmbedBuilder().WithOkColor().WithDescription(text).Build());
|
||||||
|
|
||||||
public static async Task<IUserMessage> SendConfirmAsync(this IUser user, string title, string text, string url = null)
|
public static async Task<IUserMessage> SendConfirmAsync(this IUser user, string title, string text, string url = null)
|
||||||
{
|
{
|
||||||
var eb = new EmbedBuilder().WithOkColor().WithDescription(text);
|
var eb = new EmbedBuilder().WithOkColor().WithDescription(text);
|
||||||
if (url != null && Uri.IsWellFormedUriString(url, UriKind.Absolute))
|
if (url != null && Uri.IsWellFormedUriString(url, UriKind.Absolute))
|
||||||
eb.WithUrl(url);
|
eb.WithUrl(url);
|
||||||
return await (await user.GetOrCreateDMChannelAsync()).SendMessageAsync("", embed: eb);
|
return await (await user.GetOrCreateDMChannelAsync()).SendMessageAsync("", embed: eb.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<IUserMessage> SendErrorAsync(this IUser user, string title, string error, string url = null)
|
public static async Task<IUserMessage> SendErrorAsync(this IUser user, string title, string error, string url = null)
|
||||||
@ -23,11 +24,11 @@ namespace NadekoBot.Extensions
|
|||||||
var eb = new EmbedBuilder().WithErrorColor().WithDescription(error);
|
var eb = new EmbedBuilder().WithErrorColor().WithDescription(error);
|
||||||
if (url != null && Uri.IsWellFormedUriString(url, UriKind.Absolute))
|
if (url != null && Uri.IsWellFormedUriString(url, UriKind.Absolute))
|
||||||
eb.WithUrl(url);
|
eb.WithUrl(url);
|
||||||
return await (await user.GetOrCreateDMChannelAsync()).SendMessageAsync("", embed: eb);
|
return await (await user.GetOrCreateDMChannelAsync()).SendMessageAsync("", embed: eb.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<IUserMessage> SendErrorAsync(this IUser user, string error)
|
public static async Task<IUserMessage> SendErrorAsync(this IUser user, string error)
|
||||||
=> await (await user.GetOrCreateDMChannelAsync()).SendMessageAsync("", embed: new EmbedBuilder().WithErrorColor().WithDescription(error));
|
=> await (await user.GetOrCreateDMChannelAsync()).SendMessageAsync("", embed: new EmbedBuilder().WithErrorColor().WithDescription(error).Build());
|
||||||
|
|
||||||
public static async Task<IUserMessage> SendFileAsync(this IUser user, string filePath, string caption = null, string text = null, bool isTTS = false) =>
|
public static async Task<IUserMessage> SendFileAsync(this IUser user, string filePath, string caption = null, string text = null, bool isTTS = false) =>
|
||||||
await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(File.Open(filePath, FileMode.Open), caption ?? "x", text, isTTS).ConfigureAwait(false);
|
await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(File.Open(filePath, FileMode.Open), caption ?? "x", text, isTTS).ConfigureAwait(false);
|
||||||
@ -39,5 +40,10 @@ namespace NadekoBot.Extensions
|
|||||||
usr.AvatarId.StartsWith("a_")
|
usr.AvatarId.StartsWith("a_")
|
||||||
? $"{DiscordConfig.CDNUrl}avatars/{usr.Id}/{usr.AvatarId}.gif"
|
? $"{DiscordConfig.CDNUrl}avatars/{usr.Id}/{usr.AvatarId}.gif"
|
||||||
: usr.GetAvatarUrl(ImageFormat.Auto);
|
: usr.GetAvatarUrl(ImageFormat.Auto);
|
||||||
|
|
||||||
|
public static string RealAvatarUrl(this DiscordUser usr) =>
|
||||||
|
usr.AvatarId.StartsWith("a_")
|
||||||
|
? $"{DiscordConfig.CDNUrl}avatars/{usr.UserId}/{usr.AvatarId}.gif"
|
||||||
|
: $"{DiscordConfig.CDNUrl}avatars/{usr.UserId}/{usr.AvatarId}.png";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -822,5 +822,51 @@
|
|||||||
"administration_prefix_current": "Prefix on this server is {0}",
|
"administration_prefix_current": "Prefix on this server is {0}",
|
||||||
"administration_prefix_new": "Changed prefix on this server from {0} to {1}",
|
"administration_prefix_new": "Changed prefix on this server from {0} to {1}",
|
||||||
"administration_defprefix_current": "Default bot prefix is {0}",
|
"administration_defprefix_current": "Default bot prefix is {0}",
|
||||||
"administration_defprefix_new": "Changed Default bot prefix from {0} to {1}"
|
"administration_defprefix_new": "Changed Default bot prefix from {0} to {1}",
|
||||||
|
"xp_server_level": "Server Level",
|
||||||
|
"xp_level": "Level",
|
||||||
|
"xp_club": "Club",
|
||||||
|
"xp_xp": "Experience",
|
||||||
|
"xp_excluded": "{0} has been excluded from the XP system on this server.",
|
||||||
|
"xp_not_excluded": "{0} is no longer excluded from the XP system on this server.",
|
||||||
|
"xp_exclusion_list": "Exclusion List",
|
||||||
|
"xp_server_is_excluded": "This server is excluded.",
|
||||||
|
"xp_server_is_not_excluded": "This server is not excluded.",
|
||||||
|
"xp_excluded_roles": "Excluded Roles",
|
||||||
|
"xp_excluded_channels": "Excluded Channels",
|
||||||
|
"xp_level_up_channel": "Congratulations {0}, You've reached level {1}!",
|
||||||
|
"xp_level_up_dm": "Congratulations {0}, You've reached level {1} on {2} server!",
|
||||||
|
"xp_level_up_global": "Congratulations {0}, You've reached global level {1}!",
|
||||||
|
"xp_role_reward_cleared": "Level {0} will no longer reward a role.",
|
||||||
|
"xp_role_reward_added": "Users who reach level {0} will receive {1} role.",
|
||||||
|
"xp_role_rewards": "Role Rewards",
|
||||||
|
"xp_level_x": "Level {0}",
|
||||||
|
"xp_no_role_rewards": "No role reward on this page.",
|
||||||
|
"xp_server_leaderboard": "Server XP Leaderboard",
|
||||||
|
"xp_global_leaderboard": "Global XP Leaderboard",
|
||||||
|
"xp_modified": "Modified server XP of the user {0} by {1}",
|
||||||
|
"xp_club_create_error": "Failed creating the club. Make sure you're above level 5 and not a member of a club already.",
|
||||||
|
"xp_club_created": "Club {0} successfully created!",
|
||||||
|
"xp_club_not_exists": "That club doesn't exist.",
|
||||||
|
"xp_club_applied": "You've applied for membership in {0} club.",
|
||||||
|
"xp_club_apply_error": "Error applying. You are either already a member of the club, or you don't meet the minimum level requirement, or you've been banned from this one.",
|
||||||
|
"xp_club_accepted": "Accepted user {0} to the club.",
|
||||||
|
"xp_club_accept_error": "User not found",
|
||||||
|
"xp_club_left": "You've left the club.",
|
||||||
|
"xp_club_not_in_club": "You are not in a club, or you're trying to leave the club you're the owner of.",
|
||||||
|
"xp_club_user_kick": "User {0} kicked from {1} club.",
|
||||||
|
"xp_club_user_kick_fail": "Error kicking. You're either not the club owner, or that user is not in your club.",
|
||||||
|
"xp_club_user_banned": "Banned user {0} from {1} club.",
|
||||||
|
"xp_club_user_ban_fail": "Failed to ban. You're either not the club owner, or that user is not in your club or applied to it.",
|
||||||
|
"xp_club_user_unbanned": "Unbanned user {0} in {1} club.",
|
||||||
|
"xp_club_user_unban_fail": "Failed to unban. You're either not the club owner, or that user is not in your club or applied to it.",
|
||||||
|
"xp_club_level_req_changed": "Changed club's level requirement to {0}",
|
||||||
|
"xp_club_level_req_change_error": "Failed changing level requirement.",
|
||||||
|
"xp_club_disbanded": "Club {0} has been disbanded",
|
||||||
|
"xp_club_disband_error": "Error. You are either not in a club, or you are not the owner of your club.",
|
||||||
|
"xp_club_icon_error": "Not a valid image url or you're not the club owner.",
|
||||||
|
"xp_club_icon_set": "New club icon set.",
|
||||||
|
"xp_club_bans_for": "Bans for {0} club",
|
||||||
|
"xp_club_apps_for": "Applicants for {0} club",
|
||||||
|
"xp_club_leaderboard": "Club leaderboard - page {0}"
|
||||||
}
|
}
|
BIN
src/NadekoBot/data/fonts/Uni Sans.ttf
Normal file
BIN
src/NadekoBot/data/fonts/Uni Sans.ttf
Normal file
Binary file not shown.
BIN
src/NadekoBot/data/fonts/WhitneyBold.ttf
Normal file
BIN
src/NadekoBot/data/fonts/WhitneyBold.ttf
Normal file
Binary file not shown.
BIN
src/NadekoBot/data/fonts/WhitneyBoldItalic.ttf
Normal file
BIN
src/NadekoBot/data/fonts/WhitneyBoldItalic.ttf
Normal file
Binary file not shown.
BIN
src/NadekoBot/data/images/xp/xp.png
Normal file
BIN
src/NadekoBot/data/images/xp/xp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 288 KiB |
Loading…
Reference in New Issue
Block a user