1.1.0.rc4 #345

Merged
edraft merged 9 commits from 1.1.0.rc4 into 1.1.0 2023-08-17 21:02:27 +02:00
40 changed files with 272 additions and 93 deletions
Showing only changes of commit 45a3127696 - Show all commits

View File

@ -0,0 +1,53 @@
type Discord {
guilds(filter: GuildFilter): [Guild]
users(filter: DiscordUserFilter): [DiscordUser]
}
type Guild {
id: ID
name: String
channels(filter: ChannelFilter): [Channel]
roles: [Role]
emojis: [Emoji]
}
input GuildFilter {
id: ID
name: String
}
type Channel {
id: String
name: String
type: String
}
input ChannelFilter {
id: String
name: String
type: String
}
type Role {
id: String
name: String
}
type DiscordUser {
id: String
name: String
bot: Boolean
}
input DiscordUserFilter {
id: ID
name: String
bot: Boolean
}
type Emoji {
id: String
name: String
url: String
}

View File

@ -38,7 +38,6 @@ type Query {
achievementOperators: [String]
technicianConfig: TechnicianConfig
guilds(filter: GuildFilter): [Guild]
possibleFeatureFlags: [String]
discord: Discord
}

View File

@ -37,6 +37,12 @@ from bot_graphql.queries.auto_role_rule_history_query import AutoRoleRuleHistory
from bot_graphql.queries.auto_role_rule_query import AutoRoleRuleQuery
from bot_graphql.queries.client_history_query import ClientHistoryQuery
from bot_graphql.queries.client_query import ClientQuery
from bot_graphql.queries.discord.channel_query import ChannelQuery
from bot_graphql.queries.discord.discord_query import DiscordQuery
from bot_graphql.queries.discord.discord_user_query import DiscordUserQuery
from bot_graphql.queries.discord.emoji_query import EmojiQuery
from bot_graphql.queries.discord.guild_query import GuildQuery
from bot_graphql.queries.discord.role_query import RoleQuery
from bot_graphql.queries.game_server_query import GameServerQuery
from bot_graphql.queries.known_user_history_query import KnownUserHistoryQuery
from bot_graphql.queries.known_user_query import KnownUserQuery
@ -105,6 +111,13 @@ class GraphQLModule(ModuleABC):
services.add_transient(QueryABC, UserJoinedGameServerHistoryQuery)
services.add_transient(QueryABC, UserJoinedGameServerQuery)
services.add_transient(QueryABC, DiscordQuery)
services.add_transient(QueryABC, GuildQuery)
services.add_transient(QueryABC, ChannelQuery)
services.add_transient(QueryABC, RoleQuery)
services.add_transient(QueryABC, EmojiQuery)
services.add_transient(QueryABC, DiscordUserQuery)
# filters
services.add_transient(FilterABC, AutoRoleFilter)
services.add_transient(FilterABC, AutoRoleRuleFilter)

View File

@ -0,0 +1 @@
# imports

View File

@ -1,29 +0,0 @@
type Guild {
id: ID
name: String
channels: [Channel]
roles: [Role]
emojis: [Emoji]
}
input GuildFilter {
id: ID
}
type Channel {
id: String
name: String
type: String
}
type Role {
id: String
name: String
}
type Emoji {
id: String
name: String
url: String
}

View File

@ -0,0 +1,16 @@
from cpl_query.extension import List
from discord import Guild, User
class Discord:
def __init__(self, guilds: List[Guild], users: List[User]):
self._guilds = guilds
self._users = users
@property
def guilds(self) -> List[Guild]:
return self._guilds
@property
def users(self) -> List[User]:
return self._users

View File

@ -1,10 +1,10 @@
from bot_graphql.abc.data_query_abc import DataQueryABC
from bot_graphql.abc.query_abc import QueryABC
class ChannelQuery(DataQueryABC):
class ChannelQuery(QueryABC):
def __init__(self):
DataQueryABC.__init__(self, "Channel")
QueryABC.__init__(self, "Channel")
self.set_field("id", lambda c, *_: c.id)
self.set_field("name", lambda c, *_: c.name)
self.set_field("type", lambda c, *_: type(c))
self.set_field("type", lambda c, *_: type(c).__name__)

View File

@ -0,0 +1,48 @@
from cpl_discord.service import DiscordBotServiceABC
from cpl_query.extension import List
from bot_graphql.abc.query_abc import QueryABC
class DiscordQuery(QueryABC):
def __init__(
self,
bot: DiscordBotServiceABC,
):
QueryABC.__init__(self, "Discord")
self._bot = bot
self.set_field("guilds", self._resolve_guilds)
self.set_field("users", self._resolve_users)
def _resolve_guilds(self, *_, filter=None):
guilds = self._bot.guilds
if filter is None:
return guilds
if "id" in filter:
guilds = self._bot.guilds.where(lambda g: g.id == int(filter["id"]))
if "name" in filter:
guilds = self._bot.guilds.where(lambda g: g.name == filter["name"])
return guilds
def _resolve_users(self, *_, filter=None):
users = List(any).extend(self._bot.users)
if filter is None:
return users
if "id" in filter:
users = users.where(lambda g: g.id == int(filter["id"]))
if "name" in filter:
users = users.where(lambda g: g.name == filter["name"])
if "bot" in filter:
users = users.where(lambda g: g.bot == bool(filter["bot"]))
return users

View File

@ -0,0 +1,10 @@
from bot_graphql.abc.query_abc import QueryABC
class DiscordUserQuery(QueryABC):
def __init__(self):
QueryABC.__init__(self, "DiscordUser")
self.set_field("id", lambda r, *_: r.id)
self.set_field("name", lambda r, *_: r.name)
self.set_field("bot", lambda r, *_: r.bot)

View File

@ -1,9 +1,9 @@
from bot_graphql.abc.data_query_abc import DataQueryABC
from bot_graphql.abc.query_abc import QueryABC
class RoleQuery(DataQueryABC):
class EmojiQuery(QueryABC):
def __init__(self):
DataQueryABC.__init__(self, "Emoji")
QueryABC.__init__(self, "Emoji")
self.set_field("id", lambda e, *_: e.id)
self.set_field("name", lambda e, *_: e.name)

View File

@ -1,12 +1,38 @@
from bot_graphql.abc.data_query_abc import DataQueryABC
from cpl_discord.service import DiscordBotServiceABC
from cpl_query.extension import List
from discord import Guild
from bot_graphql.abc.query_abc import QueryABC
class GuildQuery(DataQueryABC):
def __init__(self):
DataQueryABC.__init__(self, "Guild")
class GuildQuery(QueryABC):
def __init__(
self,
bot: DiscordBotServiceABC,
):
QueryABC.__init__(self, "Guild")
self._bot = bot
self.set_field("id", lambda g, *_: g.id)
self.set_field("name", lambda g, *_: g.name)
self.set_field("channels", lambda g, *_: g.channels)
self.set_field("channels", self._resolve_channels)
self.set_field("roles", lambda g, *_: g.roles)
self.set_field("emojis", lambda g, *_: g.emojis)
def _resolve_channels(self, g: Guild, *_, filter=None):
users = List(any).extend(g.channels)
if filter is None:
return users
if "id" in filter:
users = users.where(lambda c: c.id == int(filter["id"]))
if "name" in filter:
users = users.where(lambda c: c.id == filter["name"])
if "type" in filter:
users = users.where(lambda c: type(c).__name__ == filter["type"])
return users

View File

@ -1,9 +1,9 @@
from bot_graphql.abc.data_query_abc import DataQueryABC
from bot_graphql.abc.query_abc import QueryABC
class RoleQuery(DataQueryABC):
class RoleQuery(QueryABC):
def __init__(self):
DataQueryABC.__init__(self, "Role")
QueryABC.__init__(self, "Role")
self.set_field("id", lambda r, *_: r.id)
self.set_field("name", lambda r, *_: r.name)

View File

@ -1,4 +1,5 @@
from cpl_discord.service import DiscordBotServiceABC
from cpl_query.extension import List
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
from bot_data.abc.achievement_repository_abc import AchievementRepositoryABC
@ -24,6 +25,7 @@ from bot_graphql.filter.user_filter import UserFilter
from bot_graphql.filter.user_joined_game_server_filter import UserJoinedGameServerFilter
from bot_graphql.filter.user_joined_server_filter import UserJoinedServerFilter
from bot_graphql.filter.user_joined_voice_channel_filter import UserJoinedVoiceChannelFilter
from bot_graphql.model.discord import Discord
from modules.achievements.achievement_service import AchievementService
@ -47,8 +49,6 @@ class Query(QueryABC):
):
QueryABC.__init__(self, "Query")
self._bot = bot
self.add_collection("autoRole", lambda *_: auto_roles.get_auto_roles(), AutoRoleFilter)
self.add_collection("autoRoleRule", lambda *_: auto_roles.get_auto_role_rules(), AutoRoleRuleFilter)
self.add_collection("client", lambda *_: clients.get_clients(), ClientFilter)
@ -73,13 +73,7 @@ class Query(QueryABC):
self.add_collection("achievement", lambda *_: achievements.get_achievements(), AchievementFilter)
self.set_field("technicianConfig", lambda *_: technician_config.get_technician_config())
self.set_field("guilds", self._resolve_guilds)
self.set_field("achievementAttributes", lambda x, *_: achievement_service.get_attributes())
self.set_field("achievementOperators", lambda x, *_: achievement_service.get_operators())
self.set_field("possibleFeatureFlags", lambda x, *_: [e.value for e in FeatureFlagsEnum])
def _resolve_guilds(self, *_, filter=None):
if filter is None or "id" not in filter:
return self._bot.guilds
return self._bot.guilds.where(lambda g: g.id == int(filter["id"]))
self.set_field("achievementAttributes", lambda *_: achievement_service.get_attributes())
self.set_field("achievementOperators", lambda *_: achievement_service.get_operators())
self.set_field("possibleFeatureFlags", lambda *_: [e.value for e in FeatureFlagsEnum])
self.set_field("discord", lambda *_: Discord(bot.guilds, List(any).extend(bot.users)))

View File

@ -10,7 +10,7 @@ from bot_graphql.query import Query
class Schema:
def __init__(self, query: Query, mutation: Mutation, queries: list[QueryABC]):
type_defs = load_schema_from_path(os.path.join(os.path.dirname(os.path.realpath(__file__)), "model/"))
type_defs = load_schema_from_path(os.path.join(os.path.dirname(os.path.realpath(__file__)), "graphql/"))
self._schema = make_executable_schema(type_defs, query, mutation, *queries)
@property

View File

@ -1,3 +1,8 @@
export interface Discord {
guilds?: Guild[];
users?: DiscordUser[];
}
export interface Guild {
id?: string;
name?: string;
@ -14,9 +19,9 @@ export interface Channel {
}
export enum ChannelType {
category = "category",
text = "text",
voice = "voice"
category = "CategoryChannel",
text = "TextChannel",
voice = "VoiceChannel"
}
export interface Role {
@ -29,3 +34,9 @@ export interface Emoji {
name?: string;
url?: string;
}
export interface DiscordUser {
id?: string;
name?: string;
bot?: boolean;
}

View File

@ -1,25 +1,38 @@
export class Queries {
static guildsQuery = `
query GuildsQuery($id: ID) {
guilds(filter: {id: $id}) {
id
name
query GuildsQuery($id: ID, $filter: ChannelFilter) {
discord {
guilds(filter: {id: $id}) {
id
name
channels {
id
name
type
channels(filter: $filter) {
id
name
type
}
roles {
id
name
}
emojis {
id
name
url
}
}
roles {
}
}
`;
static discordUsersQuery = `
query DiscordUsersQuery {
discord {
users {
id
name
}
emojis {
id
name
url
}
}
}
`;
@ -29,10 +42,11 @@ export class Queries {
serverCount
servers(filter: $filter, page: $page, sort: $sort) {
id
discordId
name
iconURL
userCount
clients{
clients {
id
discordId
name

View File

@ -1,7 +1,7 @@
import { GameServer, Server } from "../data/server.model";
import { User } from "../data/user.model";
import { AutoRole, AutoRoleRule } from "../data/auto_role.model";
import { Guild } from "../data/discord.model";
import { Discord, Guild } from "../data/discord.model";
import { Level } from "../data/level.model";
import { Achievement, AchievementAttribute } from "../data/achievement.model";
import { TechnicianConfig } from "../config/technician-config.model";
@ -21,7 +21,7 @@ export interface ServerConfigQuery {
}
export interface SingleDiscordQuery {
guilds: Guild[];
discord: Discord;
}
export interface UserListQuery {

View File

@ -162,7 +162,7 @@
<div class="content-divider"></div>
<app-config-list translationKey="admin.settings.bot.ping_urls" [(data)]="config.pingURLs"></app-config-list>
<app-config-list translationKey="admin.settings.bot.technician_ids" [(data)]="config.technicianIds"></app-config-list>
<app-config-list translationKey="admin.settings.bot.technician_ids" [options]="possibleTechnicians" optionLabel="name" optionValue="id" [(data)]="config.technicianIds"></app-config-list>
<app-feature-flag-list [(data)]="config.featureFlags"></app-feature-flag-list>
<div class="content-row">

View File

@ -11,12 +11,13 @@ import { SpinnerService } from "src/app/services/spinner/spinner.service";
import { ToastService } from "src/app/services/toast/toast.service";
import { forkJoin, throwError } from "rxjs";
import { TechnicianConfig } from "../../../../../models/config/technician-config.model";
import { TechnicianConfigQuery } from "../../../../../models/graphql/query.model";
import { SingleDiscordQuery, TechnicianConfigQuery } from "../../../../../models/graphql/query.model";
import { Queries } from "../../../../../models/graphql/queries.model";
import { DataService } from "../../../../../services/data/data.service";
import { TechnicianConfigMutationResult } from "../../../../../models/graphql/result.model";
import { Mutations } from "../../../../../models/graphql/mutations.model";
import { AuthService } from "../../../../../services/auth/auth.service";
import { ChannelType, DiscordUser } from "../../../../../models/data/discord.model";
@Component({
@ -54,6 +55,8 @@ export class SettingsComponent implements OnInit {
technicianIds: []
};
possibleTechnicians?: DiscordUser[];
constructor(
private dataService: DataService,
private settingsService: SettingsService,
@ -76,7 +79,8 @@ export class SettingsComponent implements OnInit {
this.spinnerService.hideSpinner();
return throwError(() => error);
})),
this.dataService.query<TechnicianConfigQuery>(Queries.technicianConfigQuery)
this.dataService.query<TechnicianConfigQuery>(Queries.technicianConfigQuery),
this.dataService.query<SingleDiscordQuery>(Queries.discordUsersQuery)
]).subscribe(data => {
this.data = data[0];
this.data.webVersion = this.settingsService.getWebVersion()?.getVersionString() ?? "0.0.0";
@ -86,6 +90,7 @@ export class SettingsComponent implements OnInit {
}
this.config = data[1].technicianConfig;
this.possibleTechnicians = data[2].discord.users ?? undefined;
this.initForms();
this.spinnerService.hideSpinner();
});

View File

@ -19,10 +19,13 @@
<td style="flex: 1;">
<p-cellEditor>
<ng-template pTemplate="input">
<input class="table-edit-input" pInputText type="text" [(ngModel)]="value.value">
<p-dropdown *ngIf="options" [options]="options" [optionLabel]="optionLabel ?? ''" [optionValue]="optionValue ?? ''" [(ngModel)]="value.value"></p-dropdown>
<input *ngIf="!options" class="table-edit-input" pInputText type="text" [(ngModel)]="value.value">
</ng-template>
<ng-template pTemplate="output">
{{value.value}}
<p-dropdown *ngIf="options" [options]="options" [optionLabel]="optionLabel ?? ''" [optionValue]="optionValue ?? ''" [(ngModel)]="value.value" [disabled]="true"></p-dropdown>
<input *ngIf="!options" class="table-edit-input" pInputText type="text" [(ngModel)]="value.value" [disabled]="true">
</ng-template>
</p-cellEditor>
</td>

View File

@ -10,6 +10,9 @@ export class ConfigListComponent {
internal_data: any[] = [];
@Input() translationKey: string = "";
@Input() options?: Array<any>;
@Input() optionLabel?: string = "";
@Input() optionValue?: string = "";
@Input()
set data(val: any[]) {

View File

@ -1,10 +1,11 @@
import { Injectable } from "@angular/core";
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from "@angular/router";
import { ActivatedRoute, ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from "@angular/router";
import { catchError } from "rxjs/operators";
import { AuthService } from "src/app/services/auth/auth.service";
import { ThemeService } from "src/app/services/theme/theme.service";
import { SidebarService } from "../../../../services/sidebar/sidebar.service";
import { MemberRoles } from "../../../../models/auth/auth-user.dto";
import { DataService } from "../../../../services/data/data.service";
@Injectable({
providedIn: "root"
@ -14,7 +15,9 @@ export class AuthGuard implements CanActivate {
private router: Router,
private authService: AuthService,
private themeService: ThemeService,
private sidebarService: SidebarService
private sidebarService: SidebarService,
private data: DataService,
private route: ActivatedRoute
) {
}
@ -47,8 +50,13 @@ export class AuthGuard implements CanActivate {
if (memberRole !== undefined) {
let userHasAccess = false;
let authUser = await this.authService.getLoggedInUser();
let server = route.params["serverId"];
if (!authUser || !authUser.users) {
return true;
}
authUser?.users?.forEach(u => {
if (u.server === +(this.sidebarService.server$.value?.id ?? 0)) {
if (u.server === +(server ?? 0)) {
if (
memberRole === MemberRoles.Moderator && u.isModerator ||
memberRole === MemberRoles.Admin && u.isAdmin ||

View File

@ -105,9 +105,11 @@ export class AutoRolesRulesComponent extends ComponentWithTable implements OnIni
filter: {
id: server.discordId
}
}
},
).subscribe(data => {
this.guild = data.guilds[0];
if (data.discord.guilds) {
this.guild = data.discord.guilds[0];
}
this.emojis = this.guild.emojis
.map(x => {
return { label: x.name, value: x };

View File

@ -77,7 +77,7 @@ export class AutoRolesComponent extends ComponentWithTable implements OnInit, On
private sidebar: SidebarService,
private route: ActivatedRoute
) {
super('auto-role', ['id', 'channel_id', 'channel_name', 'message_id', 'rule_count'])
super("auto-role", ["id", "channel_id", "channel_name", "message_id", "rule_count"]);
}
public ngOnInit(): void {
@ -87,14 +87,16 @@ export class AutoRolesComponent extends ComponentWithTable implements OnInit, On
this.server = server;
this.spinner.showSpinner();
this.data.query<SingleDiscordQuery>(Queries.guildsQuery, {
id: server?.discordId,
filter: {
id: server?.discordId
type: ChannelType.text
}
}
).subscribe(data => {
this.guild = data.guilds[0];
if (data.discord.guilds) {
this.guild = data.discord.guilds[0];
}
this.channels = this.guild.channels
.filter(x => x.type === ChannelType.text)
.map(x => {
return { label: x.name, value: x };
});