Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(kook): support Kook internal api, fix #26 #95

Merged
merged 19 commits into from
Apr 25, 2023
4 changes: 4 additions & 0 deletions adapters/kook/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export default KookBot

declare global {
namespace Satori {
interface Session {
kook?: Kook.Payload & Kook.Internal
}

interface Events {
'kook/message-btn-click': {}
}
Expand Down
288 changes: 280 additions & 8 deletions adapters/kook/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,17 @@ export interface MessageMeta {
}
}

export interface PrivateChat {
code: string
last_read_time: number
latest_msg_time: number
unread_count: number
is_friend: boolean
is_blocked: boolean
is_target_blocked: boolean
target_info: User
}

export interface KmarkdownUserMeta {
id: string
username: string
Expand Down Expand Up @@ -253,8 +264,8 @@ export interface Channel {
name: string
user_id: string
guild_id: string
isCategory: number
parentId: string
is_category: number
parent_id: string
topic: string
type: number
level: number
Expand Down Expand Up @@ -356,6 +367,51 @@ interface GuildMute {
user_ids: string[]
}

export interface GuildRole {
role_id: number
name: string
color: number
position: number
hoist: 0 | 1
mentionable: 0 | 1
permissions: number
}

export enum Permissions {
GUILD_ADMIN = 0,
GUILD_MANAGE = 1,
GUILD_LOG = 2,
GUILD_INVITE_CREATE = 3,
GUILD_INVITE_MANAGE = 4,
CHANNEL_MANAGE = 5,
GUILD_USER_KICK = 6,
GUILD_USER_BAN = 7,
GUILD_EMOJI_MANAGE = 8,
GUILD_USER_NAME_CHANGE = 9,
GUILD_ROLE_MANAGE = 10,
CHANNEL_VIEW = 11,
CHANNEL_MESSAGE = 12,
CHANNEL_MANAGE_MESSAGE = 13,
CHANNEL_UPLOAD = 14,
CHANNEL_VOICE_CONNECT = 15,
CHANNEL_VOICE_MANAGE = 16,
CHANNEL_MESSAGE_AT_ALL = 17,
CHANNEL_MESSAGE_REACTION_CREATE = 18,
CHANNEL_MESSAGE_REACTION_FOLLOW = 19,
CHANNEL_VOICE_CONNECT_PASSIVE = 20,
CHANNEL_VOICE_SPEAK_KEY_ONLY = 21,
CHANNEL_VOICR_SPEAK_FREE = 22,
CHANNEL_VOICE_SPEAK = 23,
GUILD_USER_DEAFEN = 24,
GUILD_USER_NAME_CHANGE_OTHER = 25,
GUILD_USER_MUTE = 26,
CHANNEL_VOICE_BGM = 27
modcrafts marked this conversation as resolved.
Show resolved Hide resolved
}

export function hasPermission(permissions: number, permission: Permissions) {
return (permissions & (1 << permission)) === (1 << permission)
}
modcrafts marked this conversation as resolved.
Show resolved Hide resolved

namespace GuildMute {
export enum Type {
mic = 1,
Expand All @@ -376,6 +432,17 @@ interface GuildBoost {
user: User
}

interface Game {
id: number
name: string
type: 0|1|2
modcrafts marked this conversation as resolved.
Show resolved Hide resolved
options: string
kmhook_admin: boolean
process_name: string[]
product_name: string[]
icon: string
}

export interface Internal {
getGuildList(param?: Pagination): Promise<List<Guild>>
getGuildView(param: { guild_id: string }): Promise<Guild>
Expand All @@ -387,20 +454,224 @@ export interface Internal {
createGuildMute(param: { guild_id: string; user_id: string; type: GuildMute.Type }): Promise<void>
deleteGuildMute(param: { guild_id: string; user_id: string; type: GuildMute.Type }): Promise<void>
getGuildBoostHistory(param: { guild_id: string; start_time: number; end_time: number }): Promise<List<GuildBoost>>
}

getChannelList(param: { guild_id: string } & Pagination): Promise<List<Channel>>
getChannelView(param: { target_id: string }): Promise<Channel>
createChannel(param: {
guild_id: string
parent_id?: string
name: string
type?: number
limit_amount?: number
voice_quality?: string
is_category?: 0|1
}): Promise<Channel>
updateChannel(param: {
channel_id: string
name?: string
topic?: string
slow_mode?: 0|5000|10000|15000|30000|60000|120000|300000|600000|900000|1800000|3600000|7200000|21600000
}): Promise<Channel>
deleteChannel(param: { channel_id: string }): Promise<void>
getChannelUserList(param: { channel_id: string }): Promise<List<User>>
kickChannelUser(param: { channel_id: string; user_id: string}): Promise<void>
moveChannelUser(param: { target_id: string; user_ids: [] }): Promise<void>
getChannelRoleIndex(param: { channel_id: string }): Promise<{ permission_overwrites: Overwrite; permission_users: List<User>; permission_sync: 0 | 1 }>
modcrafts marked this conversation as resolved.
Show resolved Hide resolved
createChannelRole(param: { channel_id: string; type?: 'user_id'; value?: string }):
Promise<{
user_id: string
allow: number
deny: number
}>
createChannelRole(param: { channel_id: string; type: 'role_id'; value?: string }):
Promise<{
role_id: string
allow: number
deny: number
}>
updateChannelRole(param: { channel_id: string; type?: 'user_id'; value?: string; allow?: number; deny?: number }):
Promise<{
user_id: string
allow: number
deny: number
}>
updateChannelRole(param: { channel_id: string; type: 'role_id'; value?: string; allow?: number; deny?: number }):
Promise<{
role_id: string
allow: number
deny: number
}>
modcrafts marked this conversation as resolved.
Show resolved Hide resolved
deleteChannelRole(param: { channel_id: string; type?: 'role_id' | 'user_id'; value?: string }): Promise<void>

getMessageList(param: { target_id: string; msg_id?: string; pin?: 0 | 1; flag?: 'before' | 'around' | 'after' } & Pagination): Promise<List<Message>>
getMessageView(param: { msg_id: string }): Promise<Message>
createMessage(param: { type?: Type; target_id: string; content: string; quote?: string; nonce?: string; temp_target_id?: string }):
Promise<{
msg_id: string
msg_timestamp: number
nonce: string
}>
updateMessage(param: { msg_id: string; content: string; quote?: string; temp_target_id?: string }): Promise<void>
deleteMessage(param: { msg_id: string }): Promise<void>
getMessageReactionList(param: { msg_id: string; emoji: string }): Promise<User[]>
addMessageReaction(param: { msg_id: string; emoji: string }): Promise<void>
deleteMessageReaction(param: { msg_id: string; emoji: string; user_id?: string}): Promise<void>

getChannelJoinedUserList(param: { guild_id: string; user_id: string } & Pagination): Promise<List<Channel>>

getPrivateChatList(param?: Pagination): Promise<List<Omit<PrivateChat, 'is_friend' | 'is_blocked' | 'is_target_blocked'>>>
getPrivateChatView(param: { chat_code: string }): Promise<PrivateChat>
createPrivateChat(param: { target_id: string }): Promise<PrivateChat>
deletePrivateChat(param: { chat_code: string }): Promise<void>

getDirectMessageList(param: EitherOr<{ target_id: string; chat_code: string }, 'target_id', 'chat_code'> &
{ msg_id?: string; flag?: 'before' | 'around' | 'after' } & Pagination):
Promise<{ items: Message[] }>
createDirectMessage(param: { type?: Type; content: string; quote?: string; nonce?: string} &
EitherOr<{ target_id: string; chat_code: string }, 'target_id', 'chat_code'>):
Promise<{
msg_id: string
msg_timestamp: number
nonce: string
}>
updateDirectMessage(param: { msg_id: string; content: string; quote?: string}): Promise<void>
deleteDirectMessage(param: { msg_id: string }): Promise<void>
getDirectMessageReactionList(param: { msg_id: string; emoji?: string}): Promise<User[]>
addDirectMessageReaction(param: { msg_id: string; emoji: string }): Promise<void>
deleteDirectMessageReaction(param: { msg_id: string; emoji: string; user_id?: string }): Promise<void>

getGateway(param: { compress?: 0|1 }): Promise<{ url: string }>
getToken(param: { grant_type: 'authorization_code'; client_id: string; client_secret: string; code: string; redirect_uri: string}):
Promise<{
access_token: string
expires_in?: number
token_type: 'Bearer'
scope: string
}>
// createAsset(param: { file: FormData }): Promise<{ url: string }>

getUserMe(): Promise<User>
getUserView(param: { user_id: string; guild_id?: string }): Promise<User>
offline(): Promise<void>

getGuildRoleList(param: { guild_id: string } & Pagination): Promise<List<GuildRole>>
createGuildRole(param: { name?: string; guild_id: string }): Promise<GuildRole>
updateGuildRole(param: { guild_id: string; role_id: number } & Partial<Omit<GuildRole, 'role_id'>>): Promise<GuildRole>
deleteGuildRole(param: { guild_id: string; role_id: number }): Promise<void>
grantGuildRole(param: { guild_id: string; user_id?: string; role_id: number }):
Promise<{
user_id: string
guild_id: string
roles: number[]
}>
revokeGuildRole(param: { guild_id: string; user_id?: string; role_id: number }):
Promise<{
user_id: string
guild_id: string
roles: number[]
}>

getIntimacy(param: { user_id: string }):
Promise<{
img_url: string
social_info: string
last_read: number
score: number
img_list: {
id: string
url: string
}[]
}>
updateIntimacy(param: { user_id: string; score?: number; social_info?: string; img_id?: string }): Promise<void>

getGuildEmojiList(param?: Pagination): Promise<List<Emoji>>
// createGuildEmoji(param: { name?: string; guild_id: string; emoji: FormData }): Promise<Emoji>
updateGuildEmoji(param: { name: string; id: string }): Promise<void>
deleteGuildEmoji(param: { id: string }): Promise<void>

getInviteList(param: EitherOr<{ guild_id: string; channel_id: string }, 'guild_id', 'channel_id'> & Pagination):
Promise<List<{
guild_id: string
channel_id: string
url_code: string
url: string
user: User
}>>
createInvite(param: EitherOr<{ guild_id: string; channel_id: string }, 'guild_id', 'channel_id'> & { duration?: number; setting_times?: number }):
Promise<{ url: string }>
deleteInvite(param: { url_code: string; guild_id?: string; channel_id?: string }): Promise<void>

getBlacklist(param: { guild_id: string } & Pagination):
Promise<List<{
user_id: string
created_time: number
remark: string
user: User
}>>
createBlacklist(param: { guild_id: string; target_id: string; remark?: string; del_msg_days?: 0|1|2|3|4|5|6|7 }): Promise<void>
deleteBlacklist(param: { guild_id: string; target_id: string }): Promise<void>

// getGuildBadge(param: { guild_id: string; style?: 0|1|2 }): Promise<void> // 未实现
getGameList(param?: { type?: '0'|'1'|'2' }): Promise<List<Game>>
modcrafts marked this conversation as resolved.
Show resolved Hide resolved
createGame(param: { name: string; icon?: string }): Promise<List<Game>>
updateGame(param: { id: number; name?: string; icon?: string }): Promise<List<Game>>
deleteGame(param: { id: number }): Promise<void>
createGameActivity(param: { id: number; data_type: 1|2; software?: 'cloudmusic'|'qqmusic'|'kugou'; singer?: string; music_name?: string}): Promise<void>
deleteGameActivity(param: { data_type: 1|2 }): Promise<void>

hasPermission(permissions: number, permission: Permissions): boolean
}

type FilterOptional<T> = Pick<
T,
Exclude<
{
[K in keyof T]: T extends Record<K, T[K]> ? K : never;
}[keyof T],
undefined
>
>

type FilterNotOptional<T> = Pick<
T,
Exclude<
{
[K in keyof T]: T extends Record<K, T[K]> ? never : K;
}[keyof T],
undefined
>
>

type PartialEither<T, K extends keyof any> = { [P in Exclude<keyof FilterOptional<T>, K>]-?: T[P] } &
{ [P in Exclude<keyof FilterNotOptional<T>, K>]?: T[P] } &
{ [P in Extract<keyof T, K>]?: undefined }

type Object = {
[name: string]: any
}

type EitherOr<O extends Object, L extends string, R extends string> =
(
PartialEither<Pick<O, L | R>, L> |
PartialEither<Pick<O, L | R>, R>
) & Omit<O, L | R>
modcrafts marked this conversation as resolved.
Show resolved Hide resolved

export class Internal {
constructor(private http: Quester) {}
constructor(private http: Quester) {
Internal.prototype['hasPermission'] = hasPermission
}

static define(name: string, method: Quester.Method, path: string) {
Internal.prototype[name] = function (this: Internal, ...args: any[]) {
Internal.prototype[name] = async function (this: Internal, ...args: any[]) {
const config: Quester.AxiosRequestConfig = {}
if (method === 'GET' || method === 'DELETE') {
config.params = args[0]
} else {
config.data = args[0]
}
return this.http(method, path, config)
const req = await this.http(method, path, config)
if (req?.code !== 0) throw new Error(req?.message || 'Unexpected Error')
return req?.data
}
}
}
Expand All @@ -422,6 +693,7 @@ Internal.define('createChannel', 'POST', '/channel/create')
Internal.define('updateChannel', 'POST', '/channel/update')
Internal.define('deleteChannel', 'POST', '/channel/delete')
Internal.define('getChannelUserList', 'GET', '/channel/user-list')
Internal.define('kickChannelUser', 'POST', '/channel/kickout')
Internal.define('moveChannelUser', 'POST', '/channel/move-user')
Internal.define('getChannelRoleIndex', 'GET', '/channel-role/index')
Internal.define('createChannelRole', 'POST', '/channel-role/create')
Expand All @@ -437,15 +709,14 @@ Internal.define('getMessageReactionList', 'GET', '/message/reaction-list')
Internal.define('addMessageReaction', 'POST', '/message/add-reaction')
Internal.define('deleteMessageReaction', 'POST', '/message/delete-reaction')

Internal.define('getChannelUserList', 'GET', '/channel-user/get-joined-channel')
Internal.define('getChannelJoinedUserList', 'GET', '/channel-user/get-joined-channel')

Internal.define('getPrivateChatList', 'GET', '/user-chat/list')
Internal.define('getPrivateChatView', 'GET', '/user-chat/view')
Internal.define('createPrivateChat', 'POST', '/user-chat/create')
Internal.define('deletePrivateChat', 'POST', '/user-chat/delete')

Internal.define('getDirectMessageList', 'GET', '/direct-message/list')
Internal.define('getDirectMessageView', 'GET', '/direct-message/view')
Internal.define('createDirectMessage', 'POST', '/direct-message/create')
Internal.define('updateDirectMessage', 'POST', '/direct-message/update')
Internal.define('deleteDirectMessage', 'POST', '/direct-message/delete')
Expand All @@ -461,6 +732,7 @@ Internal.define('getUserMe', 'GET', '/user/me')
Internal.define('getUserView', 'GET', '/user/view')
Internal.define('offline', 'POST', '/user/offline')

Internal.define('getGuildRoleList', 'GET', '/guild-role/list')
Internal.define('createGuildRole', 'POST', '/guild-role/create')
Internal.define('updateGuildRole', 'POST', '/guild-role/update')
Internal.define('deleteGuildRole', 'POST', '/guild-role/delete')
Expand Down
4 changes: 3 additions & 1 deletion adapters/kook/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Bot, h, hyphenate, Session, Universal } from '@satorijs/satori'
import { Bot, defineProperty, h, hyphenate, Session, Universal } from '@satorijs/satori'
import * as Kook from './types'

export const adaptGroup = (data: Kook.Guild): Universal.Guild => ({
Expand Down Expand Up @@ -117,6 +117,8 @@ function adaptReaction(body: Kook.NoticeBody, session: Partial<Session>) {

export function adaptSession(bot: Bot, input: any) {
const session = bot.session()
defineProperty(session, 'kook', Object.create(bot.internal))
Object.assign(session.kook, input)
if (input.type === Kook.Type.system) {
const { type, body } = input.extra as Kook.Notice
switch (type) {
Expand Down