Skip to content

Commit

Permalink
feat(satori): support Resource encode/decode API
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Jan 2, 2025
1 parent 8f82499 commit 037cc8b
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 7 deletions.
9 changes: 3 additions & 6 deletions packages/core/src/session.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Channel, Event, GuildMember, Login, Message, User } from '@satorijs/protocol'
import { Channel, Event, GuildMember, Login, Message, Resource, User } from '@satorijs/protocol'
import { clone, defineProperty, isNullable } from 'cosmokit'
import { Context, Service } from 'cordis'
import { Bot } from './bot'
Expand Down Expand Up @@ -114,10 +114,7 @@ export class Session<C extends Context = Context> {
this.event.message.elements = isNullable(value) ? value : h.parse(value)
if (this.event.message.elements?.[0]?.type === 'quote') {
const el = this.event.message.elements.shift()
this.event.message.quote = {
...el.attrs,
content: el.children.join(''),
}
this.event.message.quote = Resource.decode(el)
}
}

Expand Down Expand Up @@ -151,7 +148,7 @@ export class Session<C extends Context = Context> {
event.message.content = this.content
delete event.message.elements
if (event.message.quote) {
event.message.content = `<quote id="${event.message.quote.id}">${event.message.quote.content}</quote>` + event.message.content
event.message.content = Resource.encode('quote', event.message.quote) + event.message.content
}
}
return event
Expand Down
58 changes: 57 additions & 1 deletion packages/protocol/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Element from '@satorijs/element'
import { Dict } from 'cosmokit'
import { Dict, isNullable, pick } from 'cosmokit'

export interface SendOptions {
linkPreview?: boolean
Expand Down Expand Up @@ -214,6 +214,62 @@ export interface User {
isBot?: boolean
}

export interface Resource<K> {
attrs: (keyof K)[]
children: (keyof K)[]
content?: keyof K
}

export function Resource<K>(attrs: (keyof K)[], children: (keyof K)[] = [], content?: keyof K): Resource<K> {
return { attrs, children, content }
}

export namespace Resource {
export interface Definitions {
user: User
member: GuildMember
channel: Channel
guild: Guild
quote: Message
}

const Definitions: { [K in keyof Definitions]: Resource<Definitions[K]> } = {
user: Resource(['id', 'name', 'nick', 'avatar', 'isBot']),
member: Resource(['name', 'nick', 'avatar']),
channel: Resource(['id', 'type', 'name']),
guild: Resource(['id', 'name', 'avatar']),
quote: Resource(['id'], ['quote', 'user', 'member', 'channel'], 'content'),
}

export function encode<K extends keyof Definitions>(type: K, data: Definitions[K]) {
const resource = Definitions[type]
const element = Element(type, pick(data, resource.attrs as any))
for (const key of resource.children) {
if (isNullable(data[key])) continue
element.children.push(encode(key as any, data[key]))
}
if (resource.content && !isNullable(data[resource.content])) {
element.children.push(...Element.parse(data[resource.content] as string))
}
return element
}

export function decode(element: Element) {
const data: any = element.attrs
const resource = Definitions[element.type]
for (const key of resource.children) {
const index = element.children.findIndex((el) => el.type === key)
if (index === -1) continue
const [child] = element.children.splice(index, 1)
data[key] = decode(child)
}
if (resource.content && element.children.length) {
data[resource.content] = element.children.join('')
}
return data
}
}

export interface GuildMember {
user?: User
name?: string
Expand Down
32 changes: 32 additions & 0 deletions packages/protocol/tests/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import Element from '@satorijs/element'
import { describe } from 'node:test'
import { Resource } from '../src'
import { expect, use } from 'chai'

describe('@satorijs/protocol', () => {
describe('Resource', () => {
expect(Resource.encode('user', {
id: '1',
name: 'Alice',
}).toString()).to.equal('<user id="1" name="Alice"/>')

expect(Resource.decode(Element.parse('<user id="1" name="Alice"/>')[0])).to.deep.equal({
id: '1',
name: 'Alice',
})

expect(Resource.encode('quote', {
id: '1',
content: '<br/>',
quote: { id: '2' },
user: { id: '3' },
}).toString()).to.equal('<quote id="1"><quote id="2"/><user id="3"/><br/></quote>')

expect(Resource.decode(Element.parse('<quote id="1"><quote id="2"/><user id="3"/><br/></quote>')[0])).to.deep.equal({
id: '1',
content: '<br/>',
quote: { id: '2' },
user: { id: '3' },
})
})
})
2 changes: 2 additions & 0 deletions yakumo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@
name: yakumo/version
- id: 16co8h
name: yakumo/publish
- id: 4q41x7
name: yakumo/test

0 comments on commit 037cc8b

Please sign in to comment.