Skip to content

Commit

Permalink
feat(rocketchat): add adapter-rocketchat
Browse files Browse the repository at this point in the history
  • Loading branch information
XxLittleCxX committed Aug 16, 2023
1 parent 1d7f04c commit 5016a53
Show file tree
Hide file tree
Showing 12 changed files with 627 additions and 0 deletions.
2 changes: 2 additions & 0 deletions adapters/rocketchat/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.DS_Store
tsconfig.tsbuildinfo
31 changes: 31 additions & 0 deletions adapters/rocketchat/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "@satorijs/adapter-rocketchat",
"description": "Rocket Chat Adapter for Satorijs",
"version": "1.0.0",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
"files": [
"lib"
],
"author": "LittleC <[email protected]>",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/satorijs/satori.git",
"directory": "adapters/rocketchat"
},
"bugs": {
"url": "https://github.com/satorijs/satori/issues"
},
"homepage": "https://koishi.chat/plugins/adapter/rocketchat.html",
"keywords": [
"bot",
"rocketchat",
"adapter",
"chatbot",
"satori"
],
"peerDependencies": {
"@satorijs/satori": "^2.6.1"
}
}
5 changes: 5 additions & 0 deletions adapters/rocketchat/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# [@satorijs/adapter-rocketchat](https://koishi.chat/plugins/adapter/rocketchat.html)

LINE adapter for [Satori](https://github.com/satorijs/satori).

- [Documentation](https://koishi.chat/plugins/adapter/rocketchat.html)
75 changes: 75 additions & 0 deletions adapters/rocketchat/src/bot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Bot, Context, omit, Quester, Schema } from '@satorijs/satori'
import { WsServer } from './ws'
import { Internal } from './internal'
import { RocketChatMessageEncoder } from './message'

export class RocketChatBot extends Bot<RocketChatBot.Config> {
static MessageEncoder = RocketChatMessageEncoder
public http: Quester
public internal: Internal
public logger = this.ctx.logger('rocketchat')
public token = ''
public endpoint: string
constructor(ctx: Context, config: RocketChatBot.Config) {
super(ctx, config)
this.endpoint = (this.config.endpoint || `https://${this.config.host}`)
this.http = ctx.http.extend({
endpoint: this.endpoint,
}).extend(config)
this.internal = new Internal(this.http)
ctx.plugin(WsServer, this)
}

callMethod(method: string, params: any[]) {
const id = Math.random().toString().slice(2)
this.socket.send(JSON.stringify({
'msg': 'method',
method,
id,
params,
}))
return id
}

subscribe(name: string, params: any[]) {
const id = Math.random().toString().slice(2)
this.socket.send(JSON.stringify({
'msg': 'sub',
name,
id,
params,
}))
return id
}

async initliaze() {
const data = await this.internal.login(this.config.username, this.config.password)
this.token = data.authToken
this.selfId = data.userId
this.http.config.headers['X-Auth-Token'] = this.token
this.http.config.headers['X-User-Id'] = this.selfId
// const statistics = await this.internal.statistics()
// this.logger.info('statistics: %s', JSON.stringify(statistics, null, 2))
// this.userId = `@${this.config.username}:${this.config.host}`
this.platform = 'rocketchat'
this.username = data.me.name
}
}

export namespace RocketChatBot {
export interface Config extends Bot.Config, Quester.Config {
username: string
password: string
endpoint: string
host: string
}
export const Config: Schema<Config> = Schema.intersect([
Schema.object({
username: Schema.string().required(),
password: Schema.string().role('secret').required(),
host: Schema.string().description('Matrix Homeserver 域名。').required(),
endpoint: Schema.string().description('Matrix Homeserver 地址。默认为 `https://{host}`。'),
...omit(Quester.Config.dict, ['endpoint']),
}),
] as const)
}
9 changes: 9 additions & 0 deletions adapters/rocketchat/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { RocketChatBot } from './bot'

export * from './bot'
export * from './utils'
export * from './message'
export * from './ws'
export * from './types'

export default RocketChatBot
25 changes: 25 additions & 0 deletions adapters/rocketchat/src/internal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Quester } from '@satorijs/satori'

export class Internal {
constructor(public http: Quester) { }

/** https://developer.rocket.chat/reference/api/realtime-api/method-calls/authentication/login */
async login(user: string, password: string) {
const { data } = await this.http.post('/api/v1/login', {
user, password,
})
return data
}

/** https://developer.rocket.chat/reference/api/rest-api/endpoints/statistics/stats-endpoints/get-statistics */
async statistics() {
const { data } = await this.http.get('/api/v1/statistics')
return data
}

/** https://developer.rocket.chat/reference/api/rest-api/endpoints/rooms/rooms-endpoints/get-rooms */
async getRooms() {
const { update } = await this.http.get('/api/v1/rooms.get')
return update
}
}
110 changes: 110 additions & 0 deletions adapters/rocketchat/src/message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { h, MessageEncoder } from '@satorijs/satori'
import { RocketChatBot } from './bot'
import FormData from 'form-data'

export const escape = (val: string) =>
val
.replace(/(?<!\u200b)[\*_~` \->[\](#!@]/g, '\u200B$&')
.replace(/([\\`*_{}])/g, '\\$&')
.replace(/([\-\*]|\d\.) /g, '\u200B$&')

export const unescape = (val: string) =>
val
.replace(/\u200b([\*_~`])/g, '$1')
export class RocketChatMessageEncoder extends MessageEncoder<RocketChatBot> {
buffer = ''
addition: Record<string, any> = {}
async flush() {
if (!this.buffer.length) return
/** https://developer.rocket.chat/reference/api/rest-api/endpoints/messaging/chat-endpoints/send-message */
await this.bot.http.post('/api/v1/chat.sendMessage', {
message: {
...this.addition,
rid: this.channelId,
// msg: this.buffer,
blocks: [{
type: 'section',
text: {
type: 'mrkdwn',
text: this.buffer,
},
}],
},
})
}

async sendAsset(element: h) {
if (this.buffer.length) await this.flush()
const { attrs } = element
const { filename, data, mime } = await this.bot.ctx.http.file(attrs.url, attrs)
const form = new FormData()
// https://github.com/form-data/form-data/issues/468
const value = process.env.KOISHI_ENV === 'browser'
? new Blob([data], { type: mime })
: Buffer.from(data)
form.append('file', value, attrs.file || filename)
// form.append('channels', this.channelId)
// if (this.thread_ts) form.append('thread_ts', this.thread_ts)
const sent = await this.bot.http.post<{
ok: boolean
file: File
}>(`/api/v1/rooms.upload/${this.channelId}`, form, {
headers: form.getHeaders(),
})
if (sent.ok) {
const session = this.bot.session()
// adaptSentAsset(sent.file, session)
session.app.emit(session, 'send', session)
this.results.push(session)
}
}

async visit(element: h) {
const { type, attrs, children } = element
if (type === 'text') {
this.buffer += escape(attrs.content)
} else if (type === 'image' && attrs.url) {
await this.sendAsset(element)
} else if (type === 'sharp' && attrs.id) {
this.buffer += ` #${attrs.id} `
} else if (type === 'at') {
if (attrs.id) this.buffer += ` @${attrs.id} `
if (attrs.type === 'all') this.buffer += ` @all `
if (attrs.type === 'here') this.buffer += ` @here `
} else if (type === 'b' || type === 'strong') {
this.buffer += '*'
await this.render(children)
this.buffer += '*'
} else if (type === 'i' || type === 'em') {
this.buffer += '_'
await this.render(children)
this.buffer += '_'
} else if (type === 's' || type === 'del') {
this.buffer += '~'
await this.render(children)
this.buffer += '~'
} else if (type === 'code') {
this.buffer += '`'
await this.render(children)
this.buffer += '`'
} else if (type === 'a') {
this.buffer += `<${attrs.href}|`
await this.render(children)
this.buffer += `>`
} else if (type === 'quote') {
this.addition.tmid = attrs.id
} else if (type === 'p') {
this.buffer += `\n`
await this.render(children)
} else if (type === 'face') {
this.buffer += `:${attrs.id}:`
} else if (type === 'author') {
this.addition = {
alias: attrs.nickname,
avatar: attrs.avatar,
}
} else if (type === 'message') {
await this.render(children)
}
}
}
Loading

0 comments on commit 5016a53

Please sign in to comment.