Merge branch 'xp-system' into 1.4
This commit is contained in:
commit
306ff3a918
@ -17,6 +17,8 @@
|
||||
CurrencyDropAmountMax,
|
||||
MinimumBetAmount,
|
||||
TriviaCurrencyReward,
|
||||
XpPerMessage,
|
||||
XpMinutesTimeout,
|
||||
|
||||
//ErrorColor, //after i fix the nadekobot.cs static variables
|
||||
//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>("XpMinutesTimeout")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasDefaultValue(5);
|
||||
|
||||
b.Property<int>("XpPerMessage")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasDefaultValue(3);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("BotConfig");
|
||||
@ -238,6 +246,63 @@ namespace NadekoBot.Migrations
|
||||
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 =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@ -391,10 +456,18 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<string>("AvatarId");
|
||||
|
||||
b.Property<int?>("ClubId");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
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<string>("Username");
|
||||
@ -403,6 +476,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.HasAlternateKey("UserId");
|
||||
|
||||
b.HasIndex("ClubId");
|
||||
|
||||
b.ToTable("DiscordUser");
|
||||
});
|
||||
|
||||
@ -445,6 +520,26 @@ namespace NadekoBot.Migrations
|
||||
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 =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@ -1252,6 +1347,35 @@ namespace NadekoBot.Migrations
|
||||
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 =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@ -1393,6 +1517,51 @@ namespace NadekoBot.Migrations
|
||||
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 =>
|
||||
{
|
||||
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig")
|
||||
@ -1442,6 +1611,40 @@ namespace NadekoBot.Migrations
|
||||
.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 =>
|
||||
{
|
||||
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
|
||||
@ -1463,6 +1666,13 @@ namespace NadekoBot.Migrations
|
||||
.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 =>
|
||||
{
|
||||
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
|
||||
@ -1470,6 +1680,13 @@ namespace NadekoBot.Migrations
|
||||
.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 =>
|
||||
{
|
||||
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
|
||||
@ -1707,6 +1924,21 @@ namespace NadekoBot.Migrations
|
||||
.WithMany("WarnPunishments")
|
||||
.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 }));
|
||||
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
|
||||
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 }));
|
||||
|
@ -153,7 +153,7 @@ namespace NadekoBot.Modules.CustomReactions
|
||||
if (Context.Guild == null) // its a private one, just send back
|
||||
await Context.Channel.SendFileAsync(txtStream, "customreactions.txt", GetText("list_all")).ConfigureAwait(false);
|
||||
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]
|
||||
|
@ -48,8 +48,8 @@ namespace NadekoBot.Modules.Gambling
|
||||
[Group]
|
||||
public class WaifuClaimCommands : NadekoSubmodule
|
||||
{
|
||||
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> _divorceCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>();
|
||||
private static ConcurrentDictionary<ulong, DateTime> _affinityCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>();
|
||||
|
||||
enum WaifuClaimResult
|
||||
{
|
||||
@ -219,7 +219,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
var now = DateTime.UtcNow;
|
||||
if (w?.Claimer == null || w.Claimer.UserId != Context.User.Id)
|
||||
result = DivorceResult.NotYourWife;
|
||||
else if (divorceCooldowns.AddOrUpdate(Context.User.Id,
|
||||
else if (_divorceCooldowns.AddOrUpdate(Context.User.Id,
|
||||
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)
|
||||
{
|
||||
}
|
||||
else if (affinityCooldowns.AddOrUpdate(Context.User.Id,
|
||||
else if (_affinityCooldowns.AddOrUpdate(Context.User.Id,
|
||||
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("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("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));
|
||||
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
|
@ -11,7 +11,7 @@ using NadekoBot.Modules.Games.Services;
|
||||
|
||||
namespace NadekoBot.Modules.Games
|
||||
{
|
||||
/*todo more games
|
||||
/* more games
|
||||
- Blackjack
|
||||
- Shiritori
|
||||
- Simple RPG adventure
|
||||
|
@ -28,7 +28,7 @@ namespace NadekoBot.Modules.Games.Services
|
||||
public readonly ImmutableArray<string> EightBallResponses;
|
||||
|
||||
private readonly Timer _t;
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly CommandHandler _cmd;
|
||||
private readonly NadekoStrings _strings;
|
||||
private readonly IImagesService _images;
|
||||
private readonly Logger _log;
|
||||
@ -38,11 +38,11 @@ namespace NadekoBot.Modules.Games.Services
|
||||
|
||||
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)
|
||||
{
|
||||
_bc = bc;
|
||||
_client = client;
|
||||
_cmd = cmd;
|
||||
_strings = strings;
|
||||
_images = images;
|
||||
_cmdHandler = cmdHandler;
|
||||
@ -59,7 +59,7 @@ namespace NadekoBot.Modules.Games.Services
|
||||
}, null, TimeSpan.FromDays(1), TimeSpan.FromDays(1));
|
||||
|
||||
//plantpick
|
||||
client.MessageReceived += PotentialFlowerGeneration;
|
||||
_cmd.OnMessageNoTrigger += PotentialFlowerGeneration;
|
||||
GenerationChannels = new ConcurrentHashSet<ulong>(gcs
|
||||
.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)
|
||||
=> _strings.GetText(key, ch.GuildId, "Games".ToLowerInvariant(), rep);
|
||||
|
||||
private Task PotentialFlowerGeneration(SocketMessage imsg)
|
||||
private Task PotentialFlowerGeneration(IUserMessage imsg)
|
||||
{
|
||||
var msg = imsg as SocketUserMessage;
|
||||
if (msg == null || msg.Author.IsBot)
|
||||
|
@ -15,6 +15,7 @@ using NadekoBot.Modules.Searches.Common;
|
||||
using NadekoBot.Modules.Searches.Services;
|
||||
using NadekoBot.Modules.NSFW.Exceptions;
|
||||
|
||||
//todo static httpclient
|
||||
namespace NadekoBot.Modules.NSFW
|
||||
{
|
||||
public class NSFW : NadekoTopLevelModule<SearchesService>
|
||||
|
@ -10,7 +10,6 @@ using System.Threading.Tasks;
|
||||
using NadekoBot.Common;
|
||||
using NadekoBot.Common.Attributes;
|
||||
|
||||
//todo 50 drawing
|
||||
namespace NadekoBot.Modules.Searches
|
||||
{
|
||||
public partial class Searches
|
||||
@ -67,319 +66,3 @@ 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
|
||||
{
|
||||
Del,
|
||||
|
@ -207,7 +207,7 @@ namespace NadekoBot.Modules.Utility.Services
|
||||
if (guildSettings.TryRemove(guild.Id, out var setting) && cleanup)
|
||||
await RescanUsers(guild).ConfigureAwait(false);
|
||||
}
|
||||
//todo multiple rescans at the same time?
|
||||
|
||||
private async Task RescanUser(IGuildUser user, StreamRoleSettings setting, IRole addRole = null)
|
||||
{
|
||||
if (user.Game.HasValue &&
|
||||
|
@ -417,7 +417,7 @@ namespace NadekoBot.Modules.Utility
|
||||
})
|
||||
});
|
||||
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]
|
||||
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>
|
||||
<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="CoreCLR-NCalc" Version="2.1.2" />
|
||||
<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.Customsearch.v1" Version="1.20.0.466" />
|
||||
<PackageReference Include="ImageSharp" Version="1.0.0-alpha9-00173" />
|
||||
<PackageReference Include="ImageSharp.Drawing" Version="1.0.0-alpha9-00168" />
|
||||
<PackageReference Include="ImageSharp" Version="1.0.0-alpha9-00194" />
|
||||
<PackageReference Include="ImageSharp.Drawing" Version="1.0.0-alpha9-00189" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="1.1.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="1.1.1" />
|
||||
@ -87,8 +87,22 @@
|
||||
<NoWarn>$(NoWarn);CS1573;CS1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" />
|
||||
<DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Resources\CommandStrings.Designer.cs">
|
||||
<SubType>Designer</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -3573,4 +3573,211 @@
|
||||
<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>
|
||||
</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>
|
||||
|
@ -39,7 +39,7 @@ namespace NadekoBot.Services
|
||||
public string DefaultPrefix { get; private set; }
|
||||
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<CommandInfo, ITextChannel, string, Task> CommandErrored = delegate { return Task.CompletedTask; };
|
||||
|
@ -24,6 +24,8 @@ namespace NadekoBot.Services.Database
|
||||
IWaifuRepository Waifus { get; }
|
||||
IDiscordUserRepository DiscordUsers { get; }
|
||||
IWarningsRepository Warnings { get; }
|
||||
IXpRepository Xp { get; }
|
||||
IClubRepository Clubs { get; }
|
||||
|
||||
int Complete();
|
||||
Task<int> CompleteAsync();
|
||||
|
@ -68,6 +68,8 @@ Nadeko Support Server: https://discord.gg/nadekobot";
|
||||
public int PermissionVersion { get; set; }
|
||||
public string DefaultPrefix { get; set; } = ".";
|
||||
public bool CustomReactionsStartWith { get; set; } = false;
|
||||
public int XpPerMessage { get; set; } = 3;
|
||||
public int XpMinutesTimeout { get; set; } = 5;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
@ -7,6 +9,22 @@
|
||||
public string Discriminator { 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() =>
|
||||
Username + "#" + Discriminator;
|
||||
}
|
||||
|
@ -86,6 +86,8 @@ namespace NadekoBot.Services.Database.Models
|
||||
|
||||
public StreamRoleSettings StreamRole { get; set; }
|
||||
|
||||
public XpSettings XpSettings { get; set; }
|
||||
|
||||
//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.Extensions;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using System;
|
||||
|
||||
namespace NadekoBot.Services.Database
|
||||
{
|
||||
@ -43,6 +44,8 @@ namespace NadekoBot.Services.Database
|
||||
public DbSet<UserPokeTypes> PokeGame { get; set; }
|
||||
public DbSet<WaifuUpdate> WaifuUpdates { get; set; }
|
||||
public DbSet<Warning> Warnings { get; set; }
|
||||
public DbSet<UserXpStats> UserXpStats { get; set; }
|
||||
public DbSet<ClubInfo> Clubs { get; set; }
|
||||
|
||||
//logging
|
||||
public DbSet<LogSetting> LogSettings { get; set; }
|
||||
@ -70,24 +73,6 @@ namespace NadekoBot.Services.Database
|
||||
{
|
||||
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>
|
||||
{
|
||||
new RaceAnimal { Icon = "🐼", Name = "Panda" },
|
||||
@ -176,7 +161,14 @@ namespace NadekoBot.Services.Database
|
||||
#endregion
|
||||
|
||||
#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
|
||||
// .HasMany(c => c.ModulePrefixes)
|
||||
// .WithOne(mp => mp.BotConfig)
|
||||
@ -277,9 +269,19 @@ namespace NadekoBot.Services.Database
|
||||
// .WithOne();
|
||||
// //.HasForeignKey<WaifuInfo>(w => w.ClaimerId)
|
||||
// //.IsRequired(false);
|
||||
#endregion
|
||||
|
||||
#region DiscordUser
|
||||
|
||||
var du = modelBuilder.Entity<DiscordUser>();
|
||||
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
|
||||
|
||||
@ -292,6 +294,63 @@ namespace NadekoBot.Services.Database
|
||||
pr.HasIndex(x => x.UserId)
|
||||
.IsUnique();
|
||||
#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);
|
||||
IEnumerable<GuildConfig> Permissionsv2ForAll(List<long> include);
|
||||
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.Blacklist)
|
||||
.Include(bc => bc.EightBallResponses)
|
||||
.Include(bc => bc.ModulePrefixes)
|
||||
.Include(bc => bc.StartupCommands)
|
||||
.Include(bc => bc.BlockedCommands)
|
||||
.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;
|
||||
|
||||
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)
|
||||
_set.Add(toReturn = new DiscordUser()
|
||||
@ -24,6 +32,7 @@ namespace NadekoBot.Services.Database.Repositories.Impl
|
||||
Discriminator = original.Discriminator,
|
||||
UserId = original.Id,
|
||||
Username = original.Username,
|
||||
Club = null,
|
||||
});
|
||||
|
||||
return toReturn;
|
||||
|
@ -189,5 +189,19 @@ namespace NadekoBot.Services.Database.Repositories.Impl
|
||||
|
||||
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;
|
||||
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)
|
||||
{
|
||||
_context = context;
|
||||
|
@ -1,11 +1,13 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Services.Database;
|
||||
using System.Linq;
|
||||
|
||||
namespace NadekoBot.Services
|
||||
{
|
||||
public class DbService
|
||||
{
|
||||
private readonly DbContextOptions options;
|
||||
private readonly DbContextOptions migrateOptions;
|
||||
|
||||
private readonly string _connectionString;
|
||||
|
||||
@ -15,25 +17,23 @@ namespace NadekoBot.Services
|
||||
var optionsBuilder = new DbContextOptionsBuilder();
|
||||
optionsBuilder.UseSqlite(creds.Db.ConnectionString);
|
||||
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()
|
||||
{
|
||||
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.Migrate();
|
||||
context.EnsureSeedData();
|
||||
|
||||
//set important sqlite stuffs
|
||||
|
@ -17,6 +17,8 @@ namespace NadekoBot.Services
|
||||
ImmutableArray<byte> WifeMatrix { get; }
|
||||
ImmutableArray<byte> RategirlDot { get; }
|
||||
|
||||
ImmutableArray<byte> XpCard { get; }
|
||||
|
||||
void Reload();
|
||||
}
|
||||
}
|
||||
|
@ -122,6 +122,18 @@ namespace NadekoBot.Services.Impl
|
||||
else
|
||||
return false;
|
||||
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:
|
||||
return false;
|
||||
}
|
||||
|
@ -377,8 +377,7 @@ namespace NadekoBot.Services.Impl
|
||||
|
||||
private string ConvertToLanguageCode(string language)
|
||||
{
|
||||
string mode;
|
||||
_languageDictionary.TryGetValue(language, out mode);
|
||||
_languageDictionary.TryGetValue(language, out var mode);
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,8 @@ namespace NadekoBot.Services.Impl
|
||||
private const string _wifeMatrixPath = _basePath + "rategirl/wifematrix.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> Tails { get; private set; }
|
||||
@ -40,6 +42,8 @@ namespace NadekoBot.Services.Impl
|
||||
public ImmutableArray<byte> WifeMatrix { get; private set; }
|
||||
public ImmutableArray<byte> RategirlDot { get; private set; }
|
||||
|
||||
public ImmutableArray<byte> XpCard { get; private set; }
|
||||
|
||||
public ImagesService()
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
@ -76,6 +80,8 @@ namespace NadekoBot.Services.Impl
|
||||
|
||||
WifeMatrix = File.ReadAllBytes(_wifeMatrixPath).ToImmutableArray();
|
||||
RategirlDot = File.ReadAllBytes(_rategirlDot).ToImmutableArray();
|
||||
|
||||
XpCard = File.ReadAllBytes(_xpCardPath).ToImmutableArray();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -20,7 +20,7 @@ namespace NadekoBot.Services.Impl
|
||||
private readonly IBotCredentials _creds;
|
||||
private readonly DateTime _started;
|
||||
|
||||
public const string BotVersion = "1.7.1";
|
||||
public const string BotVersion = "1.8";
|
||||
|
||||
public string Author => "Kwoth#2560";
|
||||
public string Library => "Discord.Net";
|
||||
|
@ -132,7 +132,7 @@ namespace NadekoBot.Extensions
|
||||
public static string ToJson<T>(this T any, Formatting formatting = Formatting.Indented) =>
|
||||
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();
|
||||
img.SaveAsPng(imageStream);
|
||||
|
@ -10,7 +10,7 @@ namespace NadekoBot.Extensions
|
||||
public static class IMessageChannelExtensions
|
||||
{
|
||||
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)
|
||||
{
|
||||
@ -20,11 +20,11 @@ namespace NadekoBot.Extensions
|
||||
eb.WithUrl(url);
|
||||
if (!string.IsNullOrWhiteSpace(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)
|
||||
=> 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)
|
||||
{
|
||||
@ -34,11 +34,11 @@ namespace NadekoBot.Extensions
|
||||
eb.WithUrl(url);
|
||||
if (!string.IsNullOrWhiteSpace(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)
|
||||
=> 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)
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Discord;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
@ -8,14 +9,14 @@ namespace NadekoBot.Extensions
|
||||
public static class IUserExtensions
|
||||
{
|
||||
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)
|
||||
{
|
||||
var eb = new EmbedBuilder().WithOkColor().WithDescription(text);
|
||||
if (url != null && Uri.IsWellFormedUriString(url, UriKind.Absolute))
|
||||
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)
|
||||
@ -23,11 +24,11 @@ namespace NadekoBot.Extensions
|
||||
var eb = new EmbedBuilder().WithErrorColor().WithDescription(error);
|
||||
if (url != null && Uri.IsWellFormedUriString(url, UriKind.Absolute))
|
||||
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)
|
||||
=> 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) =>
|
||||
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_")
|
||||
? $"{DiscordConfig.CDNUrl}avatars/{usr.Id}/{usr.AvatarId}.gif"
|
||||
: 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_new": "Changed prefix on this server from {0} to {1}",
|
||||
"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