mirror of
https://github.com/Mrs4s/go-cqhttp.git
synced 2025-06-30 11:53:25 +00:00
Compare commits
115 Commits
v1.0.0-rc4
...
v1.0.0
Author | SHA1 | Date | |
---|---|---|---|
749cde2a6d | |||
0f0e711111 | |||
233e276d6a | |||
1ab1cba84c | |||
268ac07271 | |||
c486c254d8 | |||
6ad62a2642 | |||
9762a66ba2 | |||
43c6e3dcf5 | |||
6a17c70689 | |||
d70d66d6d7 | |||
43ff36e3e8 | |||
008d546f1a | |||
82ecf19480 | |||
588728aa62 | |||
ddfe24f6db | |||
5d492c7b38 | |||
d77dc9ef64 | |||
98c2a2218a | |||
3ccc2c6087 | |||
e6e30c0a10 | |||
1815ed769d | |||
cc4a981c90 | |||
174d99f94b | |||
dd33cd9598 | |||
4ad7da7a9a | |||
73bd3c92f3 | |||
3a60e081f2 | |||
1d0b513b96 | |||
0312f05f6e | |||
b85c2ecb07 | |||
0b106d8ef5 | |||
40e4f40525 | |||
8124879c77 | |||
3f4630b6d1 | |||
6a291840d7 | |||
a0e3291725 | |||
069f9d1335 | |||
91facb54ce | |||
414f067431 | |||
a704009484 | |||
278d6260c8 | |||
485d5c0df9 | |||
e3fd0771ae | |||
8cb8428785 | |||
95adb403e9 | |||
84dcf46ae2 | |||
bef2ba6f08 | |||
04c4446496 | |||
c3840a5988 | |||
291942357b | |||
c24aa8d8a0 | |||
07b1e6b72e | |||
377d7af2c1 | |||
d867451ef6 | |||
c4d703dc86 | |||
dbddd18e3a | |||
1b8ebf55a5 | |||
63d9ffa90b | |||
2830676e3b | |||
ddd52ca933 | |||
72173337ae | |||
9b0fae6346 | |||
4ceacc38d5 | |||
1dba273b61 | |||
cb1604a098 | |||
0c9f7a1f8f | |||
edfcd41ed6 | |||
811cfdca98 | |||
0e5f3ed555 | |||
90fa530a02 | |||
c80adf5795 | |||
debc1ed1ae | |||
43dd9aa76d | |||
9c0525b3d4 | |||
cf717ad762 | |||
6b3aabd9af | |||
59ed726c6a | |||
a7c003d404 | |||
0a4f849154 | |||
17420feeac | |||
2af671cec9 | |||
0f0ccf459f | |||
2483eb09c4 | |||
a8bed3fc03 | |||
bbef330069 | |||
d96f840d7f | |||
fc0845b16d | |||
f3da083be9 | |||
06450c66a2 | |||
4ed04443c5 | |||
0be18fb221 | |||
20c62111f5 | |||
84e061f321 | |||
4d064e145f | |||
64653a6815 | |||
4497053fb9 | |||
e050fd6885 | |||
43004e2496 | |||
7d5f1d6843 | |||
960f7ab79b | |||
4cddc5051f | |||
4a80441a5c | |||
a5b51051e6 | |||
311a254b9c | |||
008e139c27 | |||
524debbfda | |||
2a4ea28f4d | |||
4061904945 | |||
37a8901061 | |||
aec0ef66be | |||
86f5b7f5f5 | |||
672dafdb9d | |||
847ef6d415 | |||
f900fd62fb |
19
.github/workflows/build_docker_image.yml
vendored
19
.github/workflows/build_docker_image.yml
vendored
@ -4,6 +4,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- 'master'
|
- 'master'
|
||||||
|
- 'dev'
|
||||||
# Sequence of patterns matched against refs/tags
|
# Sequence of patterns matched against refs/tags
|
||||||
tags:
|
tags:
|
||||||
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
|
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
|
||||||
@ -20,10 +21,10 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Set time zone
|
- name: Set time zone
|
||||||
uses: szenius/set-timezone@v1.0
|
uses: szenius/set-timezone@v1.1
|
||||||
with:
|
with:
|
||||||
timezoneLinux: "Asia/Shanghai"
|
timezoneLinux: "Asia/Shanghai"
|
||||||
timezoneMacos: "Asia/Shanghai"
|
timezoneMacos: "Asia/Shanghai"
|
||||||
@ -37,7 +38,7 @@ jobs:
|
|||||||
# password: ${{ secrets.DOCKERHUB_TOKEN }}
|
# password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Login to GHCR
|
- name: Login to GHCR
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
@ -45,7 +46,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Extract metadata (tags, labels) for Docker
|
- name: Extract metadata (tags, labels) for Docker
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v3
|
uses: docker/metadata-action@v4
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
ghcr.io/${{ github.repository }}
|
ghcr.io/${{ github.repository }}
|
||||||
@ -61,16 +62,18 @@ jobs:
|
|||||||
type=semver,pattern={{major}}
|
type=semver,pattern={{major}}
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v1
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
id: docker_build
|
id: docker_build
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
18
.github/workflows/ci.yml
vendored
18
.github/workflows/ci.yml
vendored
@ -24,18 +24,12 @@ jobs:
|
|||||||
goarch: "386"
|
goarch: "386"
|
||||||
fail-fast: true
|
fail-fast: true
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Setup Go environment
|
- name: Setup Go environment
|
||||||
uses: actions/setup-go@v2.1.3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: 1.19
|
cache: true
|
||||||
- name: Cache downloaded module
|
go-version: '1.20'
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.cache/go-build
|
|
||||||
~/go/pkg/mod
|
|
||||||
key: ${{ runner.os }}-go-${{ matrix.goos }}-${{ matrix.goarch }}-${{ hashFiles('**/go.sum') }}
|
|
||||||
- name: Build binary file
|
- name: Build binary file
|
||||||
env:
|
env:
|
||||||
GOOS: ${{ matrix.goos }}
|
GOOS: ${{ matrix.goos }}
|
||||||
@ -44,12 +38,12 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
if [ $GOOS = "windows" ]; then export BINARY_SUFFIX="$BINARY_SUFFIX.exe"; fi
|
if [ $GOOS = "windows" ]; then export BINARY_SUFFIX="$BINARY_SUFFIX.exe"; fi
|
||||||
if $IS_PR ; then echo $PR_PROMPT; fi
|
if $IS_PR ; then echo $PR_PROMPT; fi
|
||||||
export BINARY_NAME="$BINARY_PREFIX$GOOS_$GOARCH$BINARY_SUFFIX"
|
export BINARY_NAME="$BINARY_PREFIX"$GOOS"_$GOARCH$BINARY_SUFFIX"
|
||||||
export CGO_ENABLED=0
|
export CGO_ENABLED=0
|
||||||
export LD_FLAGS="-w -s -X github.com/Mrs4s/go-cqhttp/internal/base.Version=${COMMIT_ID::7}"
|
export LD_FLAGS="-w -s -X github.com/Mrs4s/go-cqhttp/internal/base.Version=${COMMIT_ID::7}"
|
||||||
go build -o "output/$BINARY_NAME" -trimpath -ldflags "$LD_FLAGS" .
|
go build -o "output/$BINARY_NAME" -trimpath -ldflags "$LD_FLAGS" .
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
if: ${{ !github.head_ref }}
|
if: ${{ !github.head_ref }}
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.goos }}_${{ matrix.goarch }}
|
name: ${{ matrix.goos }}_${{ matrix.goarch }}
|
||||||
|
8
.github/workflows/golint.yml
vendored
8
.github/workflows/golint.yml
vendored
@ -7,15 +7,15 @@ jobs:
|
|||||||
name: lint
|
name: lint
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Go environment
|
- name: Setup Go environment
|
||||||
uses: actions/setup-go@v2.1.3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: 1.19
|
go-version: '1.20'
|
||||||
|
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v2
|
uses: golangci/golangci-lint-action@v3
|
||||||
with:
|
with:
|
||||||
version: latest
|
version: latest
|
||||||
|
|
||||||
|
14
.github/workflows/release.yml
vendored
14
.github/workflows/release.yml
vendored
@ -10,20 +10,20 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2.3.4
|
run: |
|
||||||
with:
|
git version
|
||||||
fetch-depth: 0
|
git clone https://github.com/Mrs4s/go-cqhttp.git /home/runner/work/go-cqhttp/go-cqhttp
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.19'
|
go-version: '1.20'
|
||||||
|
|
||||||
- name: Run GoReleaser
|
- name: Run GoReleaser
|
||||||
uses: goreleaser/goreleaser-action@v2
|
uses: goreleaser/goreleaser-action@v4
|
||||||
with:
|
with:
|
||||||
version: latest
|
version: latest
|
||||||
args: release --rm-dist
|
args: release --clean
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -12,6 +12,10 @@ internal/btree/*.db
|
|||||||
|
|
||||||
# binary builds
|
# binary builds
|
||||||
go-cqhttp
|
go-cqhttp
|
||||||
|
*.exe
|
||||||
|
|
||||||
# macos
|
# macos
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
# windwos rc
|
||||||
|
*.syso
|
||||||
|
@ -56,6 +56,7 @@ run:
|
|||||||
skip-dirs:
|
skip-dirs:
|
||||||
- db
|
- db
|
||||||
- cmd/api-generator
|
- cmd/api-generator
|
||||||
|
- internal/encryption
|
||||||
tests: true
|
tests: true
|
||||||
|
|
||||||
# output configuration options
|
# output configuration options
|
||||||
|
@ -3,6 +3,9 @@ env:
|
|||||||
before:
|
before:
|
||||||
hooks:
|
hooks:
|
||||||
- go mod tidy
|
- go mod tidy
|
||||||
|
- go install github.com/tc-hib/go-winres@latest
|
||||||
|
- go generate winres/init.go
|
||||||
|
- go-winres make
|
||||||
release:
|
release:
|
||||||
draft: true
|
draft: true
|
||||||
discussion_category_name: General
|
discussion_category_name: General
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.19-alpine AS builder
|
FROM golang:1.20-alpine AS builder
|
||||||
|
|
||||||
RUN go env -w GO111MODULE=auto \
|
RUN go env -w GO111MODULE=auto \
|
||||||
&& go env -w CGO_ENABLED=0 \
|
&& go env -w CGO_ENABLED=0 \
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://ishkong.github.io/go-cqhttp-docs/">
|
<a href="https://ishkong.github.io/go-cqhttp-docs/">
|
||||||
<img src="https://user-images.githubusercontent.com/25968335/120111974-8abef880-c139-11eb-99cd-fa928348b198.png" width="200" height="200" alt="go-cqhttp">
|
<img src="winres/icon.png" width="200" height="200" alt="go-cqhttp">
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -10,11 +10,17 @@ import (
|
|||||||
"go/token"
|
"go/token"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var supported = flag.Bool("supported", false, "genRouter supported.go")
|
||||||
|
var output = flag.String("o", "", "output file")
|
||||||
|
var pkg = flag.String("pkg", "", "package name")
|
||||||
|
var src = flag.String("path", "", "source file")
|
||||||
|
|
||||||
type Param struct {
|
type Param struct {
|
||||||
Name string
|
Name string
|
||||||
Type string
|
Type string
|
||||||
@ -22,38 +28,77 @@ type Param struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Router struct {
|
type Router struct {
|
||||||
Func string
|
Func string
|
||||||
Path []string
|
Path []string
|
||||||
Params []Param
|
PathV11 []string // v11 only
|
||||||
|
PathV12 []string // v12 only
|
||||||
|
Params []Param
|
||||||
}
|
}
|
||||||
|
|
||||||
type generator struct {
|
type generator struct {
|
||||||
out io.Writer
|
out io.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
PathAll = 0
|
||||||
|
PathV11 = 11
|
||||||
|
PathV12 = 12
|
||||||
|
)
|
||||||
|
|
||||||
func (g *generator) WriteString(s string) {
|
func (g *generator) WriteString(s string) {
|
||||||
io.WriteString(g.out, s)
|
io.WriteString(g.out, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *generator) generate(routers []Router) {
|
func (g *generator) writef(format string, a ...any) {
|
||||||
g.WriteString("// Code generated by cmd/api-generator. DO NOT EDIT.\n\n")
|
fmt.Fprintf(g.out, format, a...)
|
||||||
g.WriteString("package api\n\nimport (\n\n")
|
|
||||||
g.WriteString("\"github.com/Mrs4s/go-cqhttp/coolq\"\n")
|
|
||||||
g.WriteString("\"github.com/Mrs4s/go-cqhttp/global\"\n")
|
|
||||||
g.WriteString(")\n\n")
|
|
||||||
g.WriteString(`func (c *Caller) call(action string, p Getter) global.MSG {
|
|
||||||
switch action {
|
|
||||||
default:
|
|
||||||
return coolq.Failed(404, "API_NOT_FOUND", "API不存在")` + "\n")
|
|
||||||
for _, router := range routers {
|
|
||||||
g.router(router)
|
|
||||||
}
|
|
||||||
io.WriteString(g.out, ` }}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *generator) router(router Router) {
|
func (g *generator) header() {
|
||||||
|
g.WriteString("// Code generated by cmd/api-generator. DO NOT EDIT.\n\n")
|
||||||
|
g.writef("package %s\n\n", *pkg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *generator) genRouter(routers []Router) {
|
||||||
|
g.WriteString("import (\n\n")
|
||||||
|
g.WriteString("\"github.com/Mrs4s/go-cqhttp/coolq\"\n")
|
||||||
|
g.WriteString("\"github.com/Mrs4s/go-cqhttp/global\"\n")
|
||||||
|
g.WriteString("\"github.com/Mrs4s/go-cqhttp/pkg/onebot\"\n")
|
||||||
|
g.WriteString(")\n\n")
|
||||||
|
g.WriteString(`func (c *Caller) call(action string, spec *onebot.Spec, p Getter) global.MSG {`)
|
||||||
|
genVer := func(path int) {
|
||||||
|
g.writef(`if spec.Version == %d {
|
||||||
|
switch action {
|
||||||
|
`, path)
|
||||||
|
for _, router := range routers {
|
||||||
|
g.router(router, path)
|
||||||
|
}
|
||||||
|
g.WriteString("}}\n")
|
||||||
|
}
|
||||||
|
genVer(PathV11)
|
||||||
|
genVer(PathV12)
|
||||||
|
// generic path
|
||||||
|
g.WriteString("switch action {\n")
|
||||||
|
for _, router := range routers {
|
||||||
|
g.router(router, PathAll)
|
||||||
|
}
|
||||||
|
g.WriteString("}\n")
|
||||||
|
g.WriteString("return coolq.Failed(404, \"API_NOT_FOUND\", \"API不存在\")}")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *generator) router(router Router, pathVersion int) {
|
||||||
|
path := router.Path
|
||||||
|
if pathVersion == PathV11 {
|
||||||
|
path = router.PathV11
|
||||||
|
}
|
||||||
|
if pathVersion == PathV12 {
|
||||||
|
path = router.PathV12
|
||||||
|
}
|
||||||
|
if len(path) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
g.WriteString(`case `)
|
g.WriteString(`case `)
|
||||||
for i, p := range router.Path {
|
for i, p := range path {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
g.WriteString(`, `)
|
g.WriteString(`, `)
|
||||||
}
|
}
|
||||||
@ -62,22 +107,29 @@ func (g *generator) router(router Router) {
|
|||||||
g.WriteString(":\n")
|
g.WriteString(":\n")
|
||||||
|
|
||||||
for i, p := range router.Params {
|
for i, p := range router.Params {
|
||||||
|
if p.Type == "*onebot.Spec" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if p.Default == "" {
|
if p.Default == "" {
|
||||||
v := "p.Get(" + strconv.Quote(p.Name) + ")"
|
v := "p.Get(" + strconv.Quote(p.Name) + ")"
|
||||||
fmt.Fprintf(g.out, "p%d := %s\n", i, conv(v, p.Type))
|
g.writef("p%d := %s\n", i, conv(v, p.Type))
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(g.out, "p%d := %s\n", i, p.Default)
|
g.writef("p%d := %s\n", i, p.Default)
|
||||||
fmt.Fprintf(g.out, "if pt := p.Get(%s); pt.Exists() {\n", strconv.Quote(p.Name))
|
g.writef("if pt := p.Get(%s); pt.Exists() {\n", strconv.Quote(p.Name))
|
||||||
fmt.Fprintf(g.out, "p%d = %s\n}\n", i, conv("pt", p.Type))
|
g.writef("p%d = %s\n}\n", i, conv("pt", p.Type))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g.WriteString("\t\treturn c.bot." + router.Func + "(")
|
g.WriteString("\t\treturn c.bot." + router.Func + "(")
|
||||||
for i := range router.Params {
|
for i, p := range router.Params {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
g.WriteString(", ")
|
g.WriteString(", ")
|
||||||
}
|
}
|
||||||
fmt.Fprintf(g.out, "p%d", i)
|
if p.Type == "*onebot.Spec" {
|
||||||
|
g.WriteString("spec")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
g.writef("p%d", i)
|
||||||
}
|
}
|
||||||
g.WriteString(")\n")
|
g.WriteString(")\n")
|
||||||
}
|
}
|
||||||
@ -85,8 +137,8 @@ func (g *generator) router(router Router) {
|
|||||||
func conv(v, t string) string {
|
func conv(v, t string) string {
|
||||||
switch t {
|
switch t {
|
||||||
default:
|
default:
|
||||||
panic("unknown type: " + t)
|
panic("unsupported type: " + t)
|
||||||
case "gjson.Result":
|
case "gjson.Result", "*onebot.Spec":
|
||||||
return v
|
return v
|
||||||
case "int64":
|
case "int64":
|
||||||
return v + ".Int()"
|
return v + ".Int()"
|
||||||
@ -100,92 +152,118 @@ func conv(v, t string) string {
|
|||||||
return v + ".Uint()"
|
return v + ".Uint()"
|
||||||
case "uint32":
|
case "uint32":
|
||||||
return "uint32(" + v + ".Uint())"
|
return "uint32(" + v + ".Uint())"
|
||||||
|
case "uint16":
|
||||||
|
return "uint16(" + v + ".Uint())"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var routers []Router
|
var routers []Router
|
||||||
src := flag.String("path", "", "source file")
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
fset := token.NewFileSet()
|
fset := token.NewFileSet()
|
||||||
file, err := parser.ParseFile(fset, *src, nil, parser.ParseComments)
|
for _, s := range strings.Split(*src, ",") {
|
||||||
if err != nil {
|
file, err := parser.ParseFile(fset, s, nil, parser.ParseComments)
|
||||||
panic(err)
|
if err != nil {
|
||||||
}
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
for _, decl := range file.Decls {
|
for _, decl := range file.Decls {
|
||||||
switch decl := decl.(type) {
|
switch decl := decl.(type) {
|
||||||
case *ast.FuncDecl:
|
case *ast.FuncDecl:
|
||||||
if !decl.Name.IsExported() || decl.Recv == nil {
|
if !decl.Name.IsExported() || decl.Recv == nil ||
|
||||||
continue
|
typeName(decl.Recv.List[0].Type) != "*CQBot" {
|
||||||
}
|
continue
|
||||||
if st, ok := decl.Recv.List[0].Type.(*ast.StarExpr); !ok || st.X.(*ast.Ident).Name != "CQBot" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
router := Router{Func: decl.Name.Name}
|
|
||||||
|
|
||||||
// compute params
|
|
||||||
for _, p := range decl.Type.Params.List {
|
|
||||||
var typ string
|
|
||||||
switch t := p.Type.(type) {
|
|
||||||
case *ast.Ident:
|
|
||||||
typ = t.Name
|
|
||||||
case *ast.SelectorExpr:
|
|
||||||
typ = t.X.(*ast.Ident).Name + "." + t.Sel.Name
|
|
||||||
}
|
}
|
||||||
for _, name := range p.Names {
|
router := Router{Func: decl.Name.Name}
|
||||||
router.Params = append(router.Params, Param{Name: snakecase(name.Name), Type: typ})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, comment := range decl.Doc.List {
|
// compute params
|
||||||
annotation, args := match(comment.Text)
|
for _, p := range decl.Type.Params.List {
|
||||||
switch annotation {
|
typ := typeName(p.Type)
|
||||||
case "route":
|
for _, name := range p.Names {
|
||||||
for _, route := range strings.Split(args, ",") {
|
router.Params = append(router.Params, Param{Name: snakecase(name.Name), Type: typ})
|
||||||
router.Path = append(router.Path, unquote(route))
|
|
||||||
}
|
}
|
||||||
case "default":
|
}
|
||||||
for name, value := range parseMap(args, "=") {
|
|
||||||
for i, p := range router.Params {
|
for _, comment := range decl.Doc.List {
|
||||||
if p.Name == name {
|
annotation, args := match(comment.Text)
|
||||||
router.Params[i].Default = convDefault(value, p.Type)
|
switch annotation {
|
||||||
}
|
case "route":
|
||||||
}
|
for _, route := range strings.Split(args, ",") {
|
||||||
}
|
router.Path = append(router.Path, unquote(route))
|
||||||
case "rename":
|
}
|
||||||
for name, value := range parseMap(args, "->") {
|
case "route11":
|
||||||
for i, p := range router.Params {
|
for _, route := range strings.Split(args, ",") {
|
||||||
if p.Name == name {
|
router.PathV11 = append(router.PathV11, unquote(route))
|
||||||
router.Params[i].Name = value
|
}
|
||||||
|
case "route12":
|
||||||
|
for _, route := range strings.Split(args, ",") {
|
||||||
|
router.PathV12 = append(router.PathV12, unquote(route))
|
||||||
|
}
|
||||||
|
case "default":
|
||||||
|
for name, value := range parseMap(args, "=") {
|
||||||
|
for i, p := range router.Params {
|
||||||
|
if p.Name == name {
|
||||||
|
router.Params[i].Default = convDefault(value, p.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "rename":
|
||||||
|
for name, value := range parseMap(args, "->") {
|
||||||
|
for i, p := range router.Params {
|
||||||
|
if p.Name == name {
|
||||||
|
router.Params[i].Name = value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sort.Slice(router.Path, func(i, j int) bool {
|
||||||
|
return router.Path[i] < router.Path[j]
|
||||||
|
})
|
||||||
|
sort.Slice(router.PathV11, func(i, j int) bool {
|
||||||
|
return router.PathV11[i] < router.PathV11[j]
|
||||||
|
})
|
||||||
|
sort.Slice(router.PathV12, func(i, j int) bool {
|
||||||
|
return router.PathV12[i] < router.PathV12[j]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if router.Path != nil || router.PathV11 != nil || router.PathV12 != nil {
|
||||||
|
routers = append(routers, router)
|
||||||
|
} else {
|
||||||
|
println(decl.Name.Name)
|
||||||
}
|
}
|
||||||
sort.Slice(router.Path, func(i, j int) bool {
|
|
||||||
return router.Path[i] < router.Path[j]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if router.Path != nil {
|
|
||||||
routers = append(routers, router)
|
|
||||||
} else {
|
|
||||||
println(decl.Name.Name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Slice(routers, func(i, j int) bool {
|
sort.Slice(routers, func(i, j int) bool {
|
||||||
return routers[i].Path[0] < routers[j].Path[0]
|
path := func(r Router) string {
|
||||||
|
if r.Path != nil {
|
||||||
|
return r.Path[0]
|
||||||
|
}
|
||||||
|
if r.PathV11 != nil {
|
||||||
|
return r.PathV11[0]
|
||||||
|
}
|
||||||
|
if r.PathV12 != nil {
|
||||||
|
return r.PathV12[0]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return path(routers[i]) < path(routers[j])
|
||||||
})
|
})
|
||||||
|
|
||||||
out := new(bytes.Buffer)
|
out := new(bytes.Buffer)
|
||||||
g := &generator{out: out}
|
g := &generator{out: out}
|
||||||
g.generate(routers)
|
g.header()
|
||||||
|
if *supported {
|
||||||
|
g.genSupported(routers)
|
||||||
|
} else {
|
||||||
|
g.genRouter(routers)
|
||||||
|
}
|
||||||
source, err := format.Source(out.Bytes())
|
source, err := format.Source(out.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
err = os.WriteFile("api.go", source, 0o644)
|
err = os.WriteFile(*output, source, 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -204,7 +282,7 @@ func unquote(s string) string {
|
|||||||
func parseMap(input string, sep string) map[string]string {
|
func parseMap(input string, sep string) map[string]string {
|
||||||
out := make(map[string]string)
|
out := make(map[string]string)
|
||||||
for _, arg := range strings.Split(input, ",") {
|
for _, arg := range strings.Split(input, ",") {
|
||||||
k, v, ok := cut(arg, sep)
|
k, v, ok := strings.Cut(arg, sep)
|
||||||
if !ok {
|
if !ok {
|
||||||
out[k] = "true"
|
out[k] = "true"
|
||||||
}
|
}
|
||||||
@ -222,20 +300,13 @@ func match(text string) (string, string) {
|
|||||||
return "", ""
|
return "", ""
|
||||||
}
|
}
|
||||||
text = strings.Trim(text, "@)")
|
text = strings.Trim(text, "@)")
|
||||||
cmd, args, ok := cut(text, "(")
|
cmd, args, ok := strings.Cut(text, "(")
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", ""
|
return "", ""
|
||||||
}
|
}
|
||||||
return cmd, unquote(args)
|
return cmd, unquote(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
func cut(s, sep string) (before, after string, found bool) {
|
|
||||||
if i := strings.Index(s, sep); i >= 0 {
|
|
||||||
return s[:i], s[i+len(sep):], true
|
|
||||||
}
|
|
||||||
return s, "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
// some abbreviations need translation before transforming ro snake case
|
// some abbreviations need translation before transforming ro snake case
|
||||||
var replacer = strings.NewReplacer("ID", "Id")
|
var replacer = strings.NewReplacer("ID", "Id")
|
||||||
|
|
||||||
@ -269,3 +340,16 @@ func convDefault(s string, t string) string {
|
|||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func typeName(x ast.Node) string {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
return x.Name
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
return typeName(x.X) + "." + x.Sel.Name
|
||||||
|
case *ast.StarExpr:
|
||||||
|
return "*" + typeName(x.X)
|
||||||
|
default:
|
||||||
|
panic("unhandled type: " + reflect.TypeOf(x).String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
44
cmd/api-generator/supported.go
Normal file
44
cmd/api-generator/supported.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "html/template"
|
||||||
|
|
||||||
|
func (g *generator) genSupported(routers []Router) {
|
||||||
|
var v11, v12 []string // for onebot v12 get_supported_actions
|
||||||
|
for _, router := range routers {
|
||||||
|
if len(router.PathV11) > 0 {
|
||||||
|
v11 = append(v11, router.PathV11...)
|
||||||
|
}
|
||||||
|
if len(router.PathV11) > 0 {
|
||||||
|
v12 = append(v12, router.PathV12...)
|
||||||
|
}
|
||||||
|
if len(router.Path) > 0 {
|
||||||
|
v11 = append(v11, router.Path...)
|
||||||
|
v12 = append(v12, router.Path...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type S struct {
|
||||||
|
V11 []string
|
||||||
|
V12 []string
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl, err := template.New("").Parse(supportedTemplete)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = tmpl.Execute(g.out, &S{V11: v11, V12: v12})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const supportedTemplete = `
|
||||||
|
var supportedV11 = []string{
|
||||||
|
{{range .V11}} "{{.}}",
|
||||||
|
{{end}}
|
||||||
|
}
|
||||||
|
|
||||||
|
var supportedV12 = []string{
|
||||||
|
{{range .V12}} "{{.}}",
|
||||||
|
{{end}}
|
||||||
|
}`
|
@ -3,33 +3,45 @@ package gocq
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/png"
|
"image/png"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Mrs4s/MiraiGo/client"
|
"github.com/Mrs4s/MiraiGo/client"
|
||||||
"github.com/Mrs4s/MiraiGo/utils"
|
"github.com/Mrs4s/MiraiGo/utils"
|
||||||
|
"github.com/Mrs4s/MiraiGo/wrapper"
|
||||||
|
"github.com/Mrs4s/go-cqhttp/internal/encryption"
|
||||||
|
_ "github.com/Mrs4s/go-cqhttp/internal/encryption/t544"
|
||||||
"github.com/mattn/go-colorable"
|
"github.com/mattn/go-colorable"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
"gopkg.ilharper.com/x/isatty"
|
"gopkg.ilharper.com/x/isatty"
|
||||||
|
|
||||||
|
"github.com/Mrs4s/go-cqhttp/internal/base"
|
||||||
|
|
||||||
"github.com/Mrs4s/go-cqhttp/global"
|
"github.com/Mrs4s/go-cqhttp/global"
|
||||||
"github.com/Mrs4s/go-cqhttp/internal/download"
|
"github.com/Mrs4s/go-cqhttp/internal/download"
|
||||||
)
|
)
|
||||||
|
|
||||||
var console = bufio.NewReader(os.Stdin)
|
var console = bufio.NewReader(os.Stdin)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
wrapper.DandelionEnergy = energy
|
||||||
|
}
|
||||||
|
|
||||||
func readLine() (str string) {
|
func readLine() (str string) {
|
||||||
str, _ = console.ReadString('\n')
|
str, _ = console.ReadString('\n')
|
||||||
str = strings.TrimSpace(str)
|
str = strings.TrimSpace(str)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func readLineTimeout(t time.Duration, de string) (str string) {
|
func readLineTimeout(t time.Duration) {
|
||||||
r := make(chan string)
|
r := make(chan string)
|
||||||
go func() {
|
go func() {
|
||||||
select {
|
select {
|
||||||
@ -37,12 +49,10 @@ func readLineTimeout(t time.Duration, de string) (str string) {
|
|||||||
case <-time.After(t):
|
case <-time.After(t):
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
str = de
|
|
||||||
select {
|
select {
|
||||||
case str = <-r:
|
case <-r:
|
||||||
case <-time.After(t):
|
case <-time.After(t):
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func readIfTTY(de string) (str string) {
|
func readIfTTY(de string) (str string) {
|
||||||
@ -54,6 +64,7 @@ func readIfTTY(de string) (str string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var cli *client.QQClient
|
var cli *client.QQClient
|
||||||
|
var device *client.DeviceInfo
|
||||||
|
|
||||||
// ErrSMSRequestError SMS请求出错
|
// ErrSMSRequestError SMS请求出错
|
||||||
var ErrSMSRequestError = errors.New("sms request error")
|
var ErrSMSRequestError = errors.New("sms request error")
|
||||||
@ -154,23 +165,15 @@ func loginResponseProcessor(res *client.LoginResponse) error {
|
|||||||
var text string
|
var text string
|
||||||
switch res.Error {
|
switch res.Error {
|
||||||
case client.SliderNeededError:
|
case client.SliderNeededError:
|
||||||
log.Warnf("登录需要滑条验证码, 请选择验证方式: ")
|
log.Warnf("登录需要滑条验证码, 请验证后重试.")
|
||||||
log.Warnf("1. 使用浏览器抓取滑条并登录")
|
ticket := getTicket(res.VerifyUrl)
|
||||||
log.Warnf("2. 使用手机QQ扫码验证 (需要手Q和gocq在同一网络下).")
|
if ticket == "" {
|
||||||
log.Warn("请输入(1 - 2):")
|
log.Infof("按 Enter 继续....")
|
||||||
text = readIfTTY("1")
|
readLine()
|
||||||
if strings.Contains(text, "1") {
|
os.Exit(0)
|
||||||
ticket := getTicket(res.VerifyUrl)
|
|
||||||
if ticket == "" {
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
res, err = cli.SubmitTicket(ticket)
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
cli.Disconnect()
|
res, err = cli.SubmitTicket(ticket)
|
||||||
cli.Release()
|
continue
|
||||||
cli = client.NewClientEmpty()
|
|
||||||
return qrcodeLogin()
|
|
||||||
case client.NeedCaptcha:
|
case client.NeedCaptcha:
|
||||||
log.Warnf("登录需要验证码.")
|
log.Warnf("登录需要验证码.")
|
||||||
_ = os.WriteFile("captcha.jpg", res.CaptchaImage, 0o644)
|
_ = os.WriteFile("captcha.jpg", res.CaptchaImage, 0o644)
|
||||||
@ -210,42 +213,44 @@ func loginResponseProcessor(res *client.LoginResponse) error {
|
|||||||
case client.UnsafeDeviceError:
|
case client.UnsafeDeviceError:
|
||||||
log.Warnf("账号已开启设备锁,请前往 -> %v <- 验证后重启Bot.", res.VerifyUrl)
|
log.Warnf("账号已开启设备锁,请前往 -> %v <- 验证后重启Bot.", res.VerifyUrl)
|
||||||
log.Infof("按 Enter 或等待 5s 后继续....")
|
log.Infof("按 Enter 或等待 5s 后继续....")
|
||||||
readLineTimeout(time.Second*5, "")
|
readLineTimeout(time.Second * 5)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
case client.OtherLoginError, client.UnknownLoginError, client.TooManySMSRequestError:
|
case client.OtherLoginError, client.UnknownLoginError, client.TooManySMSRequestError:
|
||||||
msg := res.ErrorMessage
|
msg := res.ErrorMessage
|
||||||
if strings.Contains(msg, "版本") {
|
log.Warnf("登录失败: %v Code: %v", msg, res.Code)
|
||||||
msg = "密码错误或账号被冻结"
|
if res.Code == 235 {
|
||||||
} else if strings.Contains(msg, "冻结") {
|
log.Warnf("请删除 device.json 后重试.")
|
||||||
log.Fatalf("账号被冻结")
|
|
||||||
}
|
}
|
||||||
log.Warnf("登录失败: %v", msg)
|
log.Infof("按 Enter 继续....")
|
||||||
log.Infof("按 Enter 或等待 5s 后继续....")
|
readLine()
|
||||||
readLineTimeout(time.Second*5, "")
|
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTicket(u string) (str string) {
|
func getTicket(u string) string {
|
||||||
|
log.Warnf("请选择提交滑块ticket方式:")
|
||||||
|
log.Warnf("1. 自动提交")
|
||||||
|
log.Warnf("2. 手动抓取提交")
|
||||||
|
log.Warn("请输入(1 - 2):")
|
||||||
|
text := readLine()
|
||||||
id := utils.RandomString(8)
|
id := utils.RandomString(8)
|
||||||
log.Warnf("请前往该地址验证 -> %v <- 或输入手动抓取的 ticket:(Enter 提交)", strings.ReplaceAll(u, "https://ssl.captcha.qq.com/template/wireless_mqq_captcha.html?", fmt.Sprintf("https://captcha.go-cqhttp.org/captcha?id=%v&", id)))
|
auto := !strings.Contains(text, "2")
|
||||||
manual := make(chan string, 1)
|
if auto {
|
||||||
go func() {
|
u = strings.ReplaceAll(u, "https://ssl.captcha.qq.com/template/wireless_mqq_captcha.html?", fmt.Sprintf("https://captcha.go-cqhttp.org/captcha?id=%v&", id))
|
||||||
manual <- readLine()
|
}
|
||||||
}()
|
log.Warnf("请前往该地址验证 -> %v ", u)
|
||||||
ticker := time.NewTicker(time.Second)
|
if !auto {
|
||||||
defer ticker.Stop()
|
log.Warn("请输入ticket: (Enter 提交)")
|
||||||
|
return readLine()
|
||||||
|
}
|
||||||
|
|
||||||
for count := 120; count > 0; count-- {
|
for count := 120; count > 0; count-- {
|
||||||
select {
|
str := fetchCaptcha(id)
|
||||||
case <-ticker.C:
|
if str != "" {
|
||||||
str = fetchCaptcha(id)
|
return str
|
||||||
if str != "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case str = <-manual:
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
time.Sleep(time.Second)
|
||||||
}
|
}
|
||||||
log.Warnf("验证超时")
|
log.Warnf("验证超时")
|
||||||
return ""
|
return ""
|
||||||
@ -254,7 +259,7 @@ func getTicket(u string) (str string) {
|
|||||||
func fetchCaptcha(id string) string {
|
func fetchCaptcha(id string) string {
|
||||||
g, err := download.Request{URL: "https://captcha.go-cqhttp.org/captcha/ticket?id=" + id}.JSON()
|
g, err := download.Request{URL: "https://captcha.go-cqhttp.org/captcha/ticket?id=" + id}.JSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("获取 Ticket 时出现错误: %v", err)
|
log.Debugf("获取 Ticket 时出现错误: %v", err)
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
if g.Get("ticket").Exists() {
|
if g.Get("ticket").Exists() {
|
||||||
@ -262,3 +267,34 @@ func fetchCaptcha(id string) string {
|
|||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func energy(uin uint64, id string, appVersion string, salt []byte) ([]byte, error) {
|
||||||
|
if localSigner, ok := encryption.T544Signer[appVersion]; ok {
|
||||||
|
log.Debugf("use local T544Signer v%s", appVersion)
|
||||||
|
result := localSigner(time.Now().UnixMicro(), salt)
|
||||||
|
log.Debugf("t544 sign result: %x", result)
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
log.Debugf("fallback to remote T544Signer v%s", appVersion)
|
||||||
|
signServer := "https://captcha.go-cqhttp.org/sdk/dandelion/energy"
|
||||||
|
if base.SignServerOverwrite != "" {
|
||||||
|
signServer = base.SignServerOverwrite
|
||||||
|
}
|
||||||
|
response, err := download.Request{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
URL: signServer,
|
||||||
|
Header: map[string]string{"Content-Type": "application/x-www-form-urlencoded"},
|
||||||
|
Body: bytes.NewReader([]byte(fmt.Sprintf("uin=%v&id=%s&salt=%s&version=%s", uin, id, hex.EncodeToString(salt), appVersion))),
|
||||||
|
}.Bytes()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("获取T544时出现问题: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sign, err := hex.DecodeString(gjson.GetBytes(response, "result").String())
|
||||||
|
if err != nil || len(sign) == 0 {
|
||||||
|
log.Errorf("获取T544时出现问题: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debugf("t544 sign result: %x", sign)
|
||||||
|
return sign, nil
|
||||||
|
}
|
||||||
|
@ -16,10 +16,14 @@ import (
|
|||||||
"github.com/Mrs4s/MiraiGo/client"
|
"github.com/Mrs4s/MiraiGo/client"
|
||||||
para "github.com/fumiama/go-hide-param"
|
para "github.com/fumiama/go-hide-param"
|
||||||
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
|
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
|
||||||
|
"github.com/pkg/errors"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
"golang.org/x/crypto/pbkdf2"
|
"golang.org/x/crypto/pbkdf2"
|
||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
|
|
||||||
|
"github.com/Mrs4s/go-cqhttp/internal/download"
|
||||||
|
|
||||||
"github.com/Mrs4s/go-cqhttp/coolq"
|
"github.com/Mrs4s/go-cqhttp/coolq"
|
||||||
"github.com/Mrs4s/go-cqhttp/db"
|
"github.com/Mrs4s/go-cqhttp/db"
|
||||||
"github.com/Mrs4s/go-cqhttp/global"
|
"github.com/Mrs4s/go-cqhttp/global"
|
||||||
@ -41,8 +45,12 @@ var allowStatus = [...]client.UserOnlineStatus{
|
|||||||
client.StatusGaming, client.StatusVacationing, client.StatusWatchingTV, client.StatusFitness,
|
client.StatusGaming, client.StatusVacationing, client.StatusWatchingTV, client.StatusFitness,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main 启动主程序
|
// InitBase 解析参数并检测
|
||||||
func Main() {
|
//
|
||||||
|
// 如果在 windows 下双击打开了程序,程序将在此函数释出脚本后终止;
|
||||||
|
// 如果传入 -h 参数,程序将打印帮助后终止;
|
||||||
|
// 如果传入 -d 参数,程序将在启动 daemon 后终止。
|
||||||
|
func InitBase() {
|
||||||
base.Parse()
|
base.Parse()
|
||||||
if !base.FastStart && terminal.RunningByDoubleClick() {
|
if !base.FastStart && terminal.RunningByDoubleClick() {
|
||||||
err := terminal.NoMoreDoubleClick()
|
err := terminal.NoMoreDoubleClick()
|
||||||
@ -50,7 +58,7 @@ func Main() {
|
|||||||
log.Errorf("遇到错误: %v", err)
|
log.Errorf("遇到错误: %v", err)
|
||||||
time.Sleep(time.Second * 5)
|
time.Sleep(time.Second * 5)
|
||||||
}
|
}
|
||||||
return
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case base.LittleH:
|
case base.LittleH:
|
||||||
@ -65,7 +73,10 @@ func Main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
base.Init()
|
base.Init()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrepareData 准备 log, 缓存, 数据库, 必须在 InitBase 之后执行
|
||||||
|
func PrepareData() {
|
||||||
rotateOptions := []rotatelogs.Option{
|
rotateOptions := []rotatelogs.Option{
|
||||||
rotatelogs.WithRotationTime(time.Hour * 24),
|
rotatelogs.WithRotationTime(time.Hour * 24),
|
||||||
}
|
}
|
||||||
@ -95,13 +106,17 @@ func Main() {
|
|||||||
mkCacheDir(global.VideoPath, "视频")
|
mkCacheDir(global.VideoPath, "视频")
|
||||||
mkCacheDir(global.CachePath, "发送图片")
|
mkCacheDir(global.CachePath, "发送图片")
|
||||||
mkCacheDir(path.Join(global.ImagePath, "guild-images"), "频道图片缓存")
|
mkCacheDir(path.Join(global.ImagePath, "guild-images"), "频道图片缓存")
|
||||||
|
mkCacheDir(global.VersionsPath, "版本缓存")
|
||||||
cache.Init()
|
cache.Init()
|
||||||
|
|
||||||
db.Init()
|
db.Init()
|
||||||
if err := db.Open(); err != nil {
|
if err := db.Open(); err != nil {
|
||||||
log.Fatalf("打开数据库失败: %v", err)
|
log.Fatalf("打开数据库失败: %v", err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginInteract 登录交互, 可能需要键盘输入, 必须在 InitBase, PrepareData 之后执行
|
||||||
|
func LoginInteract() {
|
||||||
var byteKey []byte
|
var byteKey []byte
|
||||||
arg := os.Args
|
arg := os.Args
|
||||||
if len(arg) > 1 {
|
if len(arg) > 1 {
|
||||||
@ -138,12 +153,13 @@ func Main() {
|
|||||||
}
|
}
|
||||||
if !global.PathExists("device.json") {
|
if !global.PathExists("device.json") {
|
||||||
log.Warn("虚拟设备信息不存在, 将自动生成随机设备.")
|
log.Warn("虚拟设备信息不存在, 将自动生成随机设备.")
|
||||||
client.GenRandomDevice()
|
device = client.GenRandomDevice()
|
||||||
_ = os.WriteFile("device.json", client.SystemDeviceInfo.ToJson(), 0o644)
|
_ = os.WriteFile("device.json", device.ToJson(), 0o644)
|
||||||
log.Info("已生成设备信息并保存到 device.json 文件.")
|
log.Info("已生成设备信息并保存到 device.json 文件.")
|
||||||
} else {
|
} else {
|
||||||
log.Info("将使用 device.json 内的设备信息运行Bot.")
|
log.Info("将使用 device.json 内的设备信息运行Bot.")
|
||||||
if err := client.SystemDeviceInfo.ReadJson([]byte(global.ReadAllText("device.json"))); err != nil {
|
device = new(client.DeviceInfo)
|
||||||
|
if err := device.ReadJson([]byte(global.ReadAllText("device.json"))); err != nil {
|
||||||
log.Fatalf("加载设备信息失败: %v", err)
|
log.Fatalf("加载设备信息失败: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -205,10 +221,27 @@ func Main() {
|
|||||||
time.Sleep(time.Second * 5)
|
time.Sleep(time.Second * 5)
|
||||||
}
|
}
|
||||||
log.Info("开始尝试登录并同步消息...")
|
log.Info("开始尝试登录并同步消息...")
|
||||||
log.Infof("使用协议: %s", client.SystemDeviceInfo.Protocol)
|
log.Infof("使用协议: %s", device.Protocol.Version())
|
||||||
cli = newClient()
|
cli = newClient()
|
||||||
|
cli.UseDevice(device)
|
||||||
isQRCodeLogin := (base.Account.Uin == 0 || len(base.Account.Password) == 0) && !base.Account.Encrypt
|
isQRCodeLogin := (base.Account.Uin == 0 || len(base.Account.Password) == 0) && !base.Account.Encrypt
|
||||||
isTokenLogin := false
|
isTokenLogin := false
|
||||||
|
|
||||||
|
if isQRCodeLogin && cli.Device().Protocol != 2 {
|
||||||
|
log.Warn("当前协议不支持二维码登录, 请配置账号密码登录.")
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载本地版本信息, 一般是在上次登录时保存的
|
||||||
|
versionFile := path.Join(global.VersionsPath, fmt.Sprint(int(cli.Device().Protocol))+".json")
|
||||||
|
if global.PathExists(versionFile) {
|
||||||
|
b, err := os.ReadFile(versionFile)
|
||||||
|
if err == nil {
|
||||||
|
_ = cli.Device().Protocol.Version().UpdateFromJson(b)
|
||||||
|
}
|
||||||
|
log.Infof("从文件 %s 读取协议版本 %v.", versionFile, cli.Device().Protocol.Version())
|
||||||
|
}
|
||||||
|
|
||||||
saveToken := func() {
|
saveToken := func() {
|
||||||
base.AccountToken = cli.GenToken()
|
base.AccountToken = cli.GenToken()
|
||||||
_ = os.WriteFile("session.token", base.AccountToken, 0o644)
|
_ = os.WriteFile("session.token", base.AccountToken, 0o644)
|
||||||
@ -239,6 +272,7 @@ func Main() {
|
|||||||
cli.Disconnect()
|
cli.Disconnect()
|
||||||
cli.Release()
|
cli.Release()
|
||||||
cli = newClient()
|
cli = newClient()
|
||||||
|
cli.UseDevice(device)
|
||||||
} else {
|
} else {
|
||||||
isTokenLogin = true
|
isTokenLogin = true
|
||||||
}
|
}
|
||||||
@ -248,6 +282,29 @@ func Main() {
|
|||||||
cli.Uin = base.Account.Uin
|
cli.Uin = base.Account.Uin
|
||||||
cli.PasswordMd5 = base.PasswordHash
|
cli.PasswordMd5 = base.PasswordHash
|
||||||
}
|
}
|
||||||
|
if !base.FastStart {
|
||||||
|
log.Infof("正在检查协议更新...")
|
||||||
|
currentVersionName := device.Protocol.Version().SortVersionName
|
||||||
|
remoteVersion, err := getRemoteLatestProtocolVersion(int(device.Protocol.Version().Protocol))
|
||||||
|
if err == nil {
|
||||||
|
remoteVersionName := gjson.GetBytes(remoteVersion, "sort_version_name").String()
|
||||||
|
if remoteVersionName != currentVersionName {
|
||||||
|
switch {
|
||||||
|
case !base.UpdateProtocol:
|
||||||
|
log.Infof("检测到协议更新: %s -> %s", currentVersionName, remoteVersionName)
|
||||||
|
log.Infof("如果登录时出现版本过低错误, 可尝试使用 -update-protocol 参数启动")
|
||||||
|
case !isTokenLogin:
|
||||||
|
_ = device.Protocol.Version().UpdateFromJson(remoteVersion)
|
||||||
|
log.Infof("协议版本已更新: %s -> %s", currentVersionName, remoteVersionName)
|
||||||
|
default:
|
||||||
|
log.Infof("检测到协议更新: %s -> %s", currentVersionName, remoteVersionName)
|
||||||
|
log.Infof("由于使用了会话缓存, 无法自动更新协议, 请删除缓存后重试")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if err.Error() != "remote version unavailable" {
|
||||||
|
log.Warnf("检查协议更新失败: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
if !isTokenLogin {
|
if !isTokenLogin {
|
||||||
if !isQRCodeLogin {
|
if !isQRCodeLogin {
|
||||||
if err := commonLogin(); err != nil {
|
if err := commonLogin(); err != nil {
|
||||||
@ -326,7 +383,13 @@ func Main() {
|
|||||||
servers.Run(coolq.NewQQBot(cli))
|
servers.Run(coolq.NewQQBot(cli))
|
||||||
log.Info("资源初始化完成, 开始处理信息.")
|
log.Info("资源初始化完成, 开始处理信息.")
|
||||||
log.Info("アトリは、高性能ですから!")
|
log.Info("アトリは、高性能ですから!")
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitSignal 在新线程检查更新和网络并等待信号, 必须在 InitBase, PrepareData, LoginInteract 之后执行
|
||||||
|
//
|
||||||
|
// - 直接返回: os.Interrupt, syscall.SIGTERM
|
||||||
|
// - dump stack: syscall.SIGQUIT, syscall.SIGUSR1
|
||||||
|
func WaitSignal() {
|
||||||
go func() {
|
go func() {
|
||||||
selfupdate.CheckUpdate()
|
selfupdate.CheckUpdate()
|
||||||
selfdiagnosis.NetworkDiagnosis(cli)
|
selfdiagnosis.NetworkDiagnosis(cli)
|
||||||
@ -389,6 +452,23 @@ func newClient() *client.QQClient {
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var remoteVersions = map[int]string{
|
||||||
|
1: "https://raw.githubusercontent.com/RomiChan/protocol-versions/master/android_phone.json",
|
||||||
|
6: "https://raw.githubusercontent.com/RomiChan/protocol-versions/master/android_pad.json",
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRemoteLatestProtocolVersion(protocolType int) ([]byte, error) {
|
||||||
|
url, ok := remoteVersions[protocolType]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("remote version unavailable")
|
||||||
|
}
|
||||||
|
response, err := download.Request{URL: url}.Bytes()
|
||||||
|
if err != nil {
|
||||||
|
return download.Request{URL: "https://ghproxy.com/" + url}.Bytes()
|
||||||
|
}
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
type protocolLogger struct{}
|
type protocolLogger struct{}
|
||||||
|
|
||||||
const fromProtocol = "Protocol -> "
|
const fromProtocol = "Protocol -> "
|
||||||
|
127
coolq/api.go
127
coolq/api.go
@ -14,12 +14,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/segmentio/asm/base64"
|
|
||||||
|
|
||||||
"github.com/Mrs4s/MiraiGo/binary"
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
"github.com/Mrs4s/MiraiGo/client"
|
"github.com/Mrs4s/MiraiGo/client"
|
||||||
"github.com/Mrs4s/MiraiGo/message"
|
"github.com/Mrs4s/MiraiGo/message"
|
||||||
"github.com/Mrs4s/MiraiGo/utils"
|
"github.com/Mrs4s/MiraiGo/utils"
|
||||||
|
"github.com/segmentio/asm/base64"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
|
|
||||||
@ -28,8 +27,10 @@ import (
|
|||||||
"github.com/Mrs4s/go-cqhttp/internal/base"
|
"github.com/Mrs4s/go-cqhttp/internal/base"
|
||||||
"github.com/Mrs4s/go-cqhttp/internal/cache"
|
"github.com/Mrs4s/go-cqhttp/internal/cache"
|
||||||
"github.com/Mrs4s/go-cqhttp/internal/download"
|
"github.com/Mrs4s/go-cqhttp/internal/download"
|
||||||
|
"github.com/Mrs4s/go-cqhttp/internal/msg"
|
||||||
"github.com/Mrs4s/go-cqhttp/internal/param"
|
"github.com/Mrs4s/go-cqhttp/internal/param"
|
||||||
"github.com/Mrs4s/go-cqhttp/modules/filter"
|
"github.com/Mrs4s/go-cqhttp/modules/filter"
|
||||||
|
"github.com/Mrs4s/go-cqhttp/pkg/onebot"
|
||||||
)
|
)
|
||||||
|
|
||||||
type guildMemberPageToken struct {
|
type guildMemberPageToken struct {
|
||||||
@ -48,7 +49,8 @@ var defaultPageToken = guildMemberPageToken{
|
|||||||
// CQGetLoginInfo 获取登录号信息
|
// CQGetLoginInfo 获取登录号信息
|
||||||
//
|
//
|
||||||
// https://git.io/Jtz1I
|
// https://git.io/Jtz1I
|
||||||
// @route(get_login_info)
|
// @route11(get_login_info)
|
||||||
|
// @route12(get_self_info)
|
||||||
func (bot *CQBot) CQGetLoginInfo() global.MSG {
|
func (bot *CQBot) CQGetLoginInfo() global.MSG {
|
||||||
return OK(global.MSG{"user_id": bot.Client.Uin, "nickname": bot.Client.Nickname})
|
return OK(global.MSG{"user_id": bot.Client.Uin, "nickname": bot.Client.Nickname})
|
||||||
}
|
}
|
||||||
@ -327,13 +329,13 @@ func (bot *CQBot) CQGetTopicChannelFeeds(guildID, channelID uint64) global.MSG {
|
|||||||
//
|
//
|
||||||
// https://git.io/Jtz1L
|
// https://git.io/Jtz1L
|
||||||
// @route(get_friend_list)
|
// @route(get_friend_list)
|
||||||
func (bot *CQBot) CQGetFriendList() global.MSG {
|
func (bot *CQBot) CQGetFriendList(spec *onebot.Spec) global.MSG {
|
||||||
fs := make([]global.MSG, 0, len(bot.Client.FriendList))
|
fs := make([]global.MSG, 0, len(bot.Client.FriendList))
|
||||||
for _, f := range bot.Client.FriendList {
|
for _, f := range bot.Client.FriendList {
|
||||||
fs = append(fs, global.MSG{
|
fs = append(fs, global.MSG{
|
||||||
"nickname": f.Nickname,
|
"nickname": f.Nickname,
|
||||||
"remark": f.Remark,
|
"remark": f.Remark,
|
||||||
"user_id": f.Uin,
|
"user_id": spec.ConvertID(f.Uin),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return OK(fs)
|
return OK(fs)
|
||||||
@ -399,14 +401,14 @@ func (bot *CQBot) CQDeleteFriend(uin int64) global.MSG {
|
|||||||
//
|
//
|
||||||
// https://git.io/Jtz1t
|
// https://git.io/Jtz1t
|
||||||
// @route(get_group_list)
|
// @route(get_group_list)
|
||||||
func (bot *CQBot) CQGetGroupList(noCache bool) global.MSG {
|
func (bot *CQBot) CQGetGroupList(noCache bool, spec *onebot.Spec) global.MSG {
|
||||||
gs := make([]global.MSG, 0, len(bot.Client.GroupList))
|
gs := make([]global.MSG, 0, len(bot.Client.GroupList))
|
||||||
if noCache {
|
if noCache {
|
||||||
_ = bot.Client.ReloadGroupList()
|
_ = bot.Client.ReloadGroupList()
|
||||||
}
|
}
|
||||||
for _, g := range bot.Client.GroupList {
|
for _, g := range bot.Client.GroupList {
|
||||||
gs = append(gs, global.MSG{
|
gs = append(gs, global.MSG{
|
||||||
"group_id": g.Code,
|
"group_id": spec.ConvertID(g.Code),
|
||||||
"group_name": g.Name,
|
"group_name": g.Name,
|
||||||
"group_create_time": g.GroupCreateTime,
|
"group_create_time": g.GroupCreateTime,
|
||||||
"group_level": g.GroupLevel,
|
"group_level": g.GroupLevel,
|
||||||
@ -421,7 +423,7 @@ func (bot *CQBot) CQGetGroupList(noCache bool) global.MSG {
|
|||||||
//
|
//
|
||||||
// https://git.io/Jtz1O
|
// https://git.io/Jtz1O
|
||||||
// @route(get_group_info)
|
// @route(get_group_info)
|
||||||
func (bot *CQBot) CQGetGroupInfo(groupID int64, noCache bool) global.MSG {
|
func (bot *CQBot) CQGetGroupInfo(groupID int64, noCache bool, spec *onebot.Spec) global.MSG {
|
||||||
group := bot.Client.FindGroup(groupID)
|
group := bot.Client.FindGroup(groupID)
|
||||||
if group == nil || noCache {
|
if group == nil || noCache {
|
||||||
group, _ = bot.Client.GetGroupInfo(groupID)
|
group, _ = bot.Client.GetGroupInfo(groupID)
|
||||||
@ -435,7 +437,7 @@ func (bot *CQBot) CQGetGroupInfo(groupID int64, noCache bool) global.MSG {
|
|||||||
for _, g := range info {
|
for _, g := range info {
|
||||||
if g.Code == groupID {
|
if g.Code == groupID {
|
||||||
return OK(global.MSG{
|
return OK(global.MSG{
|
||||||
"group_id": g.Code,
|
"group_id": spec.ConvertID(g.Code),
|
||||||
"group_name": g.Name,
|
"group_name": g.Name,
|
||||||
"group_memo": g.Memo,
|
"group_memo": g.Memo,
|
||||||
"group_create_time": 0,
|
"group_create_time": 0,
|
||||||
@ -447,7 +449,7 @@ func (bot *CQBot) CQGetGroupInfo(groupID int64, noCache bool) global.MSG {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return OK(global.MSG{
|
return OK(global.MSG{
|
||||||
"group_id": group.Code,
|
"group_id": spec.ConvertID(group.Code),
|
||||||
"group_name": group.Name,
|
"group_name": group.Name,
|
||||||
"group_create_time": group.GroupCreateTime,
|
"group_create_time": group.GroupCreateTime,
|
||||||
"group_level": group.GroupLevel,
|
"group_level": group.GroupLevel,
|
||||||
@ -693,7 +695,7 @@ func (bot *CQBot) CQGetWordSlices(content string) global.MSG {
|
|||||||
|
|
||||||
// CQSendMessage 发送消息
|
// CQSendMessage 发送消息
|
||||||
//
|
//
|
||||||
// @route(send_msg)
|
// @route11(send_msg)
|
||||||
// @rename(m->message)
|
// @rename(m->message)
|
||||||
func (bot *CQBot) CQSendMessage(groupID, userID int64, m gjson.Result, messageType string, autoEscape bool) global.MSG {
|
func (bot *CQBot) CQSendMessage(groupID, userID int64, m gjson.Result, messageType string, autoEscape bool) global.MSG {
|
||||||
switch {
|
switch {
|
||||||
@ -711,7 +713,7 @@ func (bot *CQBot) CQSendMessage(groupID, userID int64, m gjson.Result, messageTy
|
|||||||
|
|
||||||
// CQSendForwardMessage 发送合并转发消息
|
// CQSendForwardMessage 发送合并转发消息
|
||||||
//
|
//
|
||||||
// @route(send_forward_msg)
|
// @route11(send_forward_msg)
|
||||||
// @rename(m->messages)
|
// @rename(m->messages)
|
||||||
func (bot *CQBot) CQSendForwardMessage(groupID, userID int64, m gjson.Result, messageType string) global.MSG {
|
func (bot *CQBot) CQSendForwardMessage(groupID, userID int64, m gjson.Result, messageType string) global.MSG {
|
||||||
switch {
|
switch {
|
||||||
@ -730,7 +732,7 @@ func (bot *CQBot) CQSendForwardMessage(groupID, userID int64, m gjson.Result, me
|
|||||||
// CQSendGroupMessage 发送群消息
|
// CQSendGroupMessage 发送群消息
|
||||||
//
|
//
|
||||||
// https://git.io/Jtz1c
|
// https://git.io/Jtz1c
|
||||||
// @route(send_group_msg)
|
// @route11(send_group_msg)
|
||||||
// @rename(m->message)
|
// @rename(m->message)
|
||||||
func (bot *CQBot) CQSendGroupMessage(groupID int64, m gjson.Result, autoEscape bool) global.MSG {
|
func (bot *CQBot) CQSendGroupMessage(groupID int64, m gjson.Result, autoEscape bool) global.MSG {
|
||||||
group := bot.Client.FindGroup(groupID)
|
group := bot.Client.FindGroup(groupID)
|
||||||
@ -752,7 +754,7 @@ func (bot *CQBot) CQSendGroupMessage(groupID int64, m gjson.Result, autoEscape b
|
|||||||
|
|
||||||
var elem []message.IMessageElement
|
var elem []message.IMessageElement
|
||||||
if m.Type == gjson.JSON {
|
if m.Type == gjson.JSON {
|
||||||
elem = bot.ConvertObjectMessage(m, message.SourceGroup)
|
elem = bot.ConvertObjectMessage(onebot.V11, m, message.SourceGroup)
|
||||||
} else {
|
} else {
|
||||||
str := m.String()
|
str := m.String()
|
||||||
if str == "" {
|
if str == "" {
|
||||||
@ -762,7 +764,7 @@ func (bot *CQBot) CQSendGroupMessage(groupID int64, m gjson.Result, autoEscape b
|
|||||||
if autoEscape {
|
if autoEscape {
|
||||||
elem = []message.IMessageElement{message.NewText(str)}
|
elem = []message.IMessageElement{message.NewText(str)}
|
||||||
} else {
|
} else {
|
||||||
elem = bot.ConvertStringMessage(str, message.SourceGroup)
|
elem = bot.ConvertStringMessage(onebot.V11, str, message.SourceGroup)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fixAt(elem)
|
fixAt(elem)
|
||||||
@ -806,7 +808,7 @@ func (bot *CQBot) CQSendGuildChannelMessage(guildID, channelID uint64, m gjson.R
|
|||||||
|
|
||||||
var elem []message.IMessageElement
|
var elem []message.IMessageElement
|
||||||
if m.Type == gjson.JSON {
|
if m.Type == gjson.JSON {
|
||||||
elem = bot.ConvertObjectMessage(m, message.SourceGuildChannel)
|
elem = bot.ConvertObjectMessage(onebot.V11, m, message.SourceGuildChannel)
|
||||||
} else {
|
} else {
|
||||||
str := m.String()
|
str := m.String()
|
||||||
if str == "" {
|
if str == "" {
|
||||||
@ -816,7 +818,7 @@ func (bot *CQBot) CQSendGuildChannelMessage(guildID, channelID uint64, m gjson.R
|
|||||||
if autoEscape {
|
if autoEscape {
|
||||||
elem = []message.IMessageElement{message.NewText(str)}
|
elem = []message.IMessageElement{message.NewText(str)}
|
||||||
} else {
|
} else {
|
||||||
elem = bot.ConvertStringMessage(str, message.SourceGuildChannel)
|
elem = bot.ConvertStringMessage(onebot.V11, str, message.SourceGuildChannel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fixAt(elem)
|
fixAt(elem)
|
||||||
@ -850,7 +852,7 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, target int64, sourceType
|
|||||||
for i, elem := range elems {
|
for i, elem := range elems {
|
||||||
p := &elems[i]
|
p := &elems[i]
|
||||||
switch o := elem.(type) {
|
switch o := elem.(type) {
|
||||||
case *LocalVideoElement:
|
case *msg.LocalVideo:
|
||||||
w.do(func() {
|
w.do(func() {
|
||||||
gm, err := bot.uploadLocalVideo(source, o)
|
gm, err := bot.uploadLocalVideo(source, o)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -859,7 +861,7 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, target int64, sourceType
|
|||||||
*p = gm
|
*p = gm
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
case *LocalImageElement:
|
case *msg.LocalImage:
|
||||||
w.do(func() {
|
w.do(func() {
|
||||||
gm, err := bot.uploadLocalImage(source, o)
|
gm, err := bot.uploadLocalImage(source, o)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -926,7 +928,7 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, target int64, sourceType
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
content := bot.ConvertObjectMessage(c, sourceType)
|
content := bot.ConvertObjectMessage(onebot.V11, c, sourceType)
|
||||||
if uin != 0 && name != "" && len(content) > 0 {
|
if uin != 0 && name != "" && len(content) > 0 {
|
||||||
return &message.ForwardNode{
|
return &message.ForwardNode{
|
||||||
SenderId: uin,
|
SenderId: uin,
|
||||||
@ -962,7 +964,7 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, target int64, sourceType
|
|||||||
// CQSendGroupForwardMessage 扩展API-发送合并转发(群)
|
// CQSendGroupForwardMessage 扩展API-发送合并转发(群)
|
||||||
//
|
//
|
||||||
// https://docs.go-cqhttp.org/api/#%E5%8F%91%E9%80%81%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91-%E7%BE%A4
|
// https://docs.go-cqhttp.org/api/#%E5%8F%91%E9%80%81%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91-%E7%BE%A4
|
||||||
// @route(send_group_forward_msg)
|
// @route11(send_group_forward_msg)
|
||||||
// @rename(m->messages)
|
// @rename(m->messages)
|
||||||
func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) global.MSG {
|
func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) global.MSG {
|
||||||
if m.Type != gjson.JSON {
|
if m.Type != gjson.JSON {
|
||||||
@ -989,7 +991,7 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) globa
|
|||||||
// CQSendPrivateForwardMessage 扩展API-发送合并转发(好友)
|
// CQSendPrivateForwardMessage 扩展API-发送合并转发(好友)
|
||||||
//
|
//
|
||||||
// https://docs.go-cqhttp.org/api/#%E5%8F%91%E9%80%81%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91-%E7%BE%A4
|
// https://docs.go-cqhttp.org/api/#%E5%8F%91%E9%80%81%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91-%E7%BE%A4
|
||||||
// @route(send_private_forward_msg)
|
// @route11(send_private_forward_msg)
|
||||||
// @rename(m->messages)
|
// @rename(m->messages)
|
||||||
func (bot *CQBot) CQSendPrivateForwardMessage(userID int64, m gjson.Result) global.MSG {
|
func (bot *CQBot) CQSendPrivateForwardMessage(userID int64, m gjson.Result) global.MSG {
|
||||||
if m.Type != gjson.JSON {
|
if m.Type != gjson.JSON {
|
||||||
@ -1014,12 +1016,12 @@ func (bot *CQBot) CQSendPrivateForwardMessage(userID int64, m gjson.Result) glob
|
|||||||
// CQSendPrivateMessage 发送私聊消息
|
// CQSendPrivateMessage 发送私聊消息
|
||||||
//
|
//
|
||||||
// https://git.io/Jtz1l
|
// https://git.io/Jtz1l
|
||||||
// @route(send_private_msg)
|
// @route11(send_private_msg)
|
||||||
// @rename(m->message)
|
// @rename(m->message)
|
||||||
func (bot *CQBot) CQSendPrivateMessage(userID int64, groupID int64, m gjson.Result, autoEscape bool) global.MSG {
|
func (bot *CQBot) CQSendPrivateMessage(userID int64, groupID int64, m gjson.Result, autoEscape bool) global.MSG {
|
||||||
var elem []message.IMessageElement
|
var elem []message.IMessageElement
|
||||||
if m.Type == gjson.JSON {
|
if m.Type == gjson.JSON {
|
||||||
elem = bot.ConvertObjectMessage(m, message.SourcePrivate)
|
elem = bot.ConvertObjectMessage(onebot.V11, m, message.SourcePrivate)
|
||||||
} else {
|
} else {
|
||||||
str := m.String()
|
str := m.String()
|
||||||
if str == "" {
|
if str == "" {
|
||||||
@ -1028,7 +1030,7 @@ func (bot *CQBot) CQSendPrivateMessage(userID int64, groupID int64, m gjson.Resu
|
|||||||
if autoEscape {
|
if autoEscape {
|
||||||
elem = []message.IMessageElement{message.NewText(str)}
|
elem = []message.IMessageElement{message.NewText(str)}
|
||||||
} else {
|
} else {
|
||||||
elem = bot.ConvertStringMessage(str, message.SourcePrivate)
|
elem = bot.ConvertStringMessage(onebot.V11, str, message.SourcePrivate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mid := bot.SendPrivateMessage(userID, groupID, &message.SendingMessage{Elements: elem})
|
mid := bot.SendPrivateMessage(userID, groupID, &message.SendingMessage{Elements: elem})
|
||||||
@ -1107,12 +1109,12 @@ func (bot *CQBot) CQSetGroupMemo(groupID int64, msg, img string) global.MSG {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return Failed(100, "IMAGE_NOT_FOUND", "图片未找到")
|
return Failed(100, "IMAGE_NOT_FOUND", "图片未找到")
|
||||||
}
|
}
|
||||||
err = bot.Client.AddGroupNoticeWithPic(groupID, msg, data)
|
_, err = bot.Client.AddGroupNoticeWithPic(groupID, msg, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Failed(100, "SEND_NOTICE_ERROR", err.Error())
|
return Failed(100, "SEND_NOTICE_ERROR", err.Error())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err := bot.Client.AddGroupNoticeSimple(groupID, msg)
|
_, err := bot.Client.AddGroupNoticeSimple(groupID, msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Failed(100, "SEND_NOTICE_ERROR", err.Error())
|
return Failed(100, "SEND_NOTICE_ERROR", err.Error())
|
||||||
}
|
}
|
||||||
@ -1167,7 +1169,7 @@ func (bot *CQBot) CQSetGroupBan(groupID, userID int64, duration uint32) global.M
|
|||||||
if m := g.FindMember(userID); m != nil {
|
if m := g.FindMember(userID); m != nil {
|
||||||
err := m.Mute(duration)
|
err := m.Mute(duration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if duration > 2592000 {
|
if duration >= 2592000 {
|
||||||
return Failed(100, "DURATION_IS_NOT_IN_RANGE", "非法的禁言时长")
|
return Failed(100, "DURATION_IS_NOT_IN_RANGE", "非法的禁言时长")
|
||||||
}
|
}
|
||||||
return Failed(100, "NOT_MANAGEABLE", "机器人权限不足")
|
return Failed(100, "NOT_MANAGEABLE", "机器人权限不足")
|
||||||
@ -1338,6 +1340,19 @@ func (bot *CQBot) CQSetGroupAdmin(groupID, userID int64, enable bool) global.MSG
|
|||||||
return OK(nil)
|
return OK(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CQSetGroupAnonymous 群组匿名
|
||||||
|
//
|
||||||
|
// https://beautyyu.one
|
||||||
|
// @route(set_group_anonymous)
|
||||||
|
// @default(enable=true)
|
||||||
|
func (bot *CQBot) CQSetGroupAnonymous(groupID int64, enable bool) global.MSG {
|
||||||
|
if g := bot.Client.FindGroup(groupID); g != nil {
|
||||||
|
g.SetAnonymous(enable)
|
||||||
|
return OK(nil)
|
||||||
|
}
|
||||||
|
return Failed(100, "GROUP_NOT_FOUND", "群聊不存在")
|
||||||
|
}
|
||||||
|
|
||||||
// CQGetGroupHonorInfo 获取群荣誉信息
|
// CQGetGroupHonorInfo 获取群荣誉信息
|
||||||
//
|
//
|
||||||
// https://git.io/Jtz1H
|
// https://git.io/Jtz1H
|
||||||
@ -1400,7 +1415,8 @@ func (bot *CQBot) CQGetGroupHonorInfo(groupID int64, t string) global.MSG {
|
|||||||
// CQGetStrangerInfo 获取陌生人信息
|
// CQGetStrangerInfo 获取陌生人信息
|
||||||
//
|
//
|
||||||
// https://git.io/Jtz17
|
// https://git.io/Jtz17
|
||||||
// @route(get_stranger_info)
|
// @route11(get_stranger_info)
|
||||||
|
// @route12(get_user_info)
|
||||||
func (bot *CQBot) CQGetStrangerInfo(userID int64) global.MSG {
|
func (bot *CQBot) CQGetStrangerInfo(userID int64) global.MSG {
|
||||||
info, err := bot.Client.GetSummaryInfo(userID)
|
info, err := bot.Client.GetSummaryInfo(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1419,6 +1435,7 @@ func (bot *CQBot) CQGetStrangerInfo(userID int64) global.MSG {
|
|||||||
// unknown = 0x2
|
// unknown = 0x2
|
||||||
return "unknown"
|
return "unknown"
|
||||||
}(),
|
}(),
|
||||||
|
"sign": info.Sign,
|
||||||
"age": info.Age,
|
"age": info.Age,
|
||||||
"level": info.Level,
|
"level": info.Level,
|
||||||
"login_days": info.LoginDays,
|
"login_days": info.LoginDays,
|
||||||
@ -1429,7 +1446,7 @@ func (bot *CQBot) CQGetStrangerInfo(userID int64) global.MSG {
|
|||||||
// CQHandleQuickOperation 隐藏API-对事件执行快速操作
|
// CQHandleQuickOperation 隐藏API-对事件执行快速操作
|
||||||
//
|
//
|
||||||
// https://git.io/Jtz15
|
// https://git.io/Jtz15
|
||||||
// @route(".handle_quick_operation")
|
// @route11(".handle_quick_operation")
|
||||||
func (bot *CQBot) CQHandleQuickOperation(context, operation gjson.Result) global.MSG {
|
func (bot *CQBot) CQHandleQuickOperation(context, operation gjson.Result) global.MSG {
|
||||||
postType := context.Get("post_type").Str
|
postType := context.Get("post_type").Str
|
||||||
|
|
||||||
@ -1811,7 +1828,7 @@ func (bot *CQBot) CQGetOnlineClients(noCache bool) global.MSG {
|
|||||||
// CQCanSendImage 检查是否可以发送图片(此处永远返回true)
|
// CQCanSendImage 检查是否可以发送图片(此处永远返回true)
|
||||||
//
|
//
|
||||||
// https://git.io/Jtz1N
|
// https://git.io/Jtz1N
|
||||||
// @route(can_send_image)
|
// @route11(can_send_image)
|
||||||
func (bot *CQBot) CQCanSendImage() global.MSG {
|
func (bot *CQBot) CQCanSendImage() global.MSG {
|
||||||
return OK(global.MSG{"yes": true})
|
return OK(global.MSG{"yes": true})
|
||||||
}
|
}
|
||||||
@ -1819,7 +1836,7 @@ func (bot *CQBot) CQCanSendImage() global.MSG {
|
|||||||
// CQCanSendRecord 检查是否可以发送语音(此处永远返回true)
|
// CQCanSendRecord 检查是否可以发送语音(此处永远返回true)
|
||||||
//
|
//
|
||||||
// https://git.io/Jtz1x
|
// https://git.io/Jtz1x
|
||||||
// @route(can_send_record)
|
// @route11(can_send_record)
|
||||||
func (bot *CQBot) CQCanSendRecord() global.MSG {
|
func (bot *CQBot) CQCanSendRecord() global.MSG {
|
||||||
return OK(global.MSG{"yes": true})
|
return OK(global.MSG{"yes": true})
|
||||||
}
|
}
|
||||||
@ -1830,7 +1847,11 @@ func (bot *CQBot) CQCanSendRecord() global.MSG {
|
|||||||
// @route(ocr_image,".ocr_image")
|
// @route(ocr_image,".ocr_image")
|
||||||
// @rename(image_id->image)
|
// @rename(image_id->image)
|
||||||
func (bot *CQBot) CQOcrImage(imageID string) global.MSG {
|
func (bot *CQBot) CQOcrImage(imageID string) global.MSG {
|
||||||
img, err := bot.makeImageOrVideoElem(map[string]string{"file": imageID}, false, message.SourceGroup)
|
// TODO: fix this
|
||||||
|
var elem msg.Element
|
||||||
|
elem.Type = "image"
|
||||||
|
elem.Data = []msg.Pair{{K: "file", V: imageID}}
|
||||||
|
img, err := bot.makeImageOrVideoElem(elem, false, message.SourceGroup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("load image error: %v", err)
|
log.Warnf("load image error: %v", err)
|
||||||
return Failed(100, "LOAD_FILE_ERROR", err.Error())
|
return Failed(100, "LOAD_FILE_ERROR", err.Error())
|
||||||
@ -1887,15 +1908,22 @@ func (bot *CQBot) CQSetGroupAnonymousBan(groupID int64, flag string, duration in
|
|||||||
//
|
//
|
||||||
// https://git.io/JtzMe
|
// https://git.io/JtzMe
|
||||||
// @route(get_status)
|
// @route(get_status)
|
||||||
func (bot *CQBot) CQGetStatus() global.MSG {
|
func (bot *CQBot) CQGetStatus(spec *onebot.Spec) global.MSG {
|
||||||
|
if spec.Version == 11 {
|
||||||
|
return OK(global.MSG{
|
||||||
|
"app_initialized": true,
|
||||||
|
"app_enabled": true,
|
||||||
|
"plugins_good": nil,
|
||||||
|
"app_good": true,
|
||||||
|
"online": bot.Client.Online.Load(),
|
||||||
|
"good": bot.Client.Online.Load(),
|
||||||
|
"stat": bot.Client.GetStatistics(),
|
||||||
|
})
|
||||||
|
}
|
||||||
return OK(global.MSG{
|
return OK(global.MSG{
|
||||||
"app_initialized": true,
|
"online": bot.Client.Online.Load(),
|
||||||
"app_enabled": true,
|
"good": bot.Client.Online.Load(),
|
||||||
"plugins_good": nil,
|
"stat": bot.Client.GetStatistics(),
|
||||||
"app_good": true,
|
|
||||||
"online": bot.Client.Online.Load(),
|
|
||||||
"good": bot.Client.Online.Load(),
|
|
||||||
"stat": bot.Client.GetStatistics(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1973,7 +2001,7 @@ func (bot *CQBot) CQCheckURLSafely(url string) global.MSG {
|
|||||||
// CQGetVersionInfo 获取版本信息
|
// CQGetVersionInfo 获取版本信息
|
||||||
//
|
//
|
||||||
// https://git.io/JtwUs
|
// https://git.io/JtwUs
|
||||||
// @route(get_version_info)
|
// @route11(get_version_info)
|
||||||
func (bot *CQBot) CQGetVersionInfo() global.MSG {
|
func (bot *CQBot) CQGetVersionInfo() global.MSG {
|
||||||
wd, _ := os.Getwd()
|
wd, _ := os.Getwd()
|
||||||
return OK(global.MSG{
|
return OK(global.MSG{
|
||||||
@ -1990,7 +2018,7 @@ func (bot *CQBot) CQGetVersionInfo() global.MSG {
|
|||||||
"runtime_version": runtime.Version(),
|
"runtime_version": runtime.Version(),
|
||||||
"runtime_os": runtime.GOOS,
|
"runtime_os": runtime.GOOS,
|
||||||
"version": base.Version,
|
"version": base.Version,
|
||||||
"protocol_name": client.SystemDeviceInfo.Protocol,
|
"protocol_name": bot.Client.Device().Protocol,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2083,9 +2111,16 @@ func (bot *CQBot) CQReloadEventFilter(file string) global.MSG {
|
|||||||
return OK(nil)
|
return OK(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CQGetSupportedActions 获取支持的动作列表
|
||||||
|
//
|
||||||
|
// @route(get_supported_actions)
|
||||||
|
func (bot *CQBot) CQGetSupportedActions(spec *onebot.Spec) global.MSG {
|
||||||
|
return OK(spec.SupportedActions)
|
||||||
|
}
|
||||||
|
|
||||||
// OK 生成成功返回值
|
// OK 生成成功返回值
|
||||||
func OK(data interface{}) global.MSG {
|
func OK(data any) global.MSG {
|
||||||
return global.MSG{"data": data, "retcode": 0, "status": "ok"}
|
return global.MSG{"data": data, "retcode": 0, "status": "ok", "message": ""}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Failed 生成失败返回值
|
// Failed 生成失败返回值
|
||||||
@ -2097,7 +2132,7 @@ func Failed(code int, msg ...string) global.MSG {
|
|||||||
if len(msg) > 1 {
|
if len(msg) > 1 {
|
||||||
w = msg[1]
|
w = msg[1]
|
||||||
}
|
}
|
||||||
return global.MSG{"data": nil, "retcode": code, "msg": m, "wording": w, "status": "failed"}
|
return global.MSG{"data": nil, "retcode": code, "msg": m, "wording": w, "message": w, "status": "failed"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func limitedString(str string) string {
|
func limitedString(str string) string {
|
||||||
|
34
coolq/api_v12.go
Normal file
34
coolq/api_v12.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package coolq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
|
|
||||||
|
"github.com/Mrs4s/go-cqhttp/global"
|
||||||
|
"github.com/Mrs4s/go-cqhttp/internal/base"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CQGetVersion 获取版本信息 OneBotV12
|
||||||
|
//
|
||||||
|
// https://git.io/JtwUs
|
||||||
|
// @route12(get_version)
|
||||||
|
func (bot *CQBot) CQGetVersion() global.MSG {
|
||||||
|
return OK(global.MSG{
|
||||||
|
"impl": "go_cqhttp",
|
||||||
|
"platform": "qq",
|
||||||
|
"version": base.Version,
|
||||||
|
"onebot_version": 12,
|
||||||
|
"runtime_version": runtime.Version(),
|
||||||
|
"runtime_os": runtime.GOOS,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CQSendMessageV12 发送消息
|
||||||
|
//
|
||||||
|
// @route12(send_message)
|
||||||
|
// @rename(m->message)
|
||||||
|
func (bot *CQBot) CQSendMessageV12(groupID, userID, detailType string, m gjson.Result) global.MSG {
|
||||||
|
// TODO: implement
|
||||||
|
return OK(nil)
|
||||||
|
}
|
41
coolq/bot.go
41
coolq/bot.go
@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"image/png"
|
||||||
"os"
|
"os"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strings"
|
"strings"
|
||||||
@ -19,11 +20,14 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/segmentio/asm/base64"
|
"github.com/segmentio/asm/base64"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/image/webp"
|
||||||
|
|
||||||
"github.com/Mrs4s/go-cqhttp/db"
|
"github.com/Mrs4s/go-cqhttp/db"
|
||||||
"github.com/Mrs4s/go-cqhttp/global"
|
"github.com/Mrs4s/go-cqhttp/global"
|
||||||
"github.com/Mrs4s/go-cqhttp/internal/base"
|
"github.com/Mrs4s/go-cqhttp/internal/base"
|
||||||
"github.com/Mrs4s/go-cqhttp/internal/mime"
|
"github.com/Mrs4s/go-cqhttp/internal/mime"
|
||||||
|
"github.com/Mrs4s/go-cqhttp/internal/msg"
|
||||||
|
"github.com/Mrs4s/go-cqhttp/pkg/onebot"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CQBot CQBot结构体,存储Bot实例相关配置
|
// CQBot CQBot结构体,存储Bot实例相关配置
|
||||||
@ -112,7 +116,7 @@ func NewQQBot(cli *client.QQClient) *CQBot {
|
|||||||
for {
|
for {
|
||||||
<-t.C
|
<-t.C
|
||||||
bot.dispatchEvent("meta_event/heartbeat", global.MSG{
|
bot.dispatchEvent("meta_event/heartbeat", global.MSG{
|
||||||
"status": bot.CQGetStatus()["data"],
|
"status": bot.CQGetStatus(onebot.V11)["data"],
|
||||||
"interval": base.HeartbeatInterval.Milliseconds(),
|
"interval": base.HeartbeatInterval.Milliseconds(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -144,7 +148,7 @@ func (w *worker) wait() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// uploadLocalImage 上传本地图片
|
// uploadLocalImage 上传本地图片
|
||||||
func (bot *CQBot) uploadLocalImage(target message.Source, img *LocalImageElement) (message.IMessageElement, error) {
|
func (bot *CQBot) uploadLocalImage(target message.Source, img *msg.LocalImage) (message.IMessageElement, error) {
|
||||||
if img.File != "" {
|
if img.File != "" {
|
||||||
f, err := os.Open(img.File)
|
f, err := os.Open(img.File)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -153,10 +157,23 @@ func (bot *CQBot) uploadLocalImage(target message.Source, img *LocalImageElement
|
|||||||
defer func() { _ = f.Close() }()
|
defer func() { _ = f.Close() }()
|
||||||
img.Stream = f
|
img.Stream = f
|
||||||
}
|
}
|
||||||
if mt, ok := mime.CheckImage(img.Stream); !ok {
|
mt, ok := mime.CheckImage(img.Stream)
|
||||||
|
if !ok {
|
||||||
return nil, errors.New("image type error: " + mt)
|
return nil, errors.New("image type error: " + mt)
|
||||||
}
|
}
|
||||||
i, err := bot.Client.UploadImage(target, img.Stream, 4)
|
if mt == "image/webp" && base.ConvertWebpImage {
|
||||||
|
img0, err := webp.Decode(img.Stream)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "decode webp error")
|
||||||
|
}
|
||||||
|
stream := bytes.NewBuffer(nil)
|
||||||
|
err = png.Encode(stream, img0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "encode png error")
|
||||||
|
}
|
||||||
|
img.Stream = bytes.NewReader(stream.Bytes())
|
||||||
|
}
|
||||||
|
i, err := bot.Client.UploadImage(target, img.Stream)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -171,20 +188,20 @@ func (bot *CQBot) uploadLocalImage(target message.Source, img *LocalImageElement
|
|||||||
}
|
}
|
||||||
|
|
||||||
// uploadLocalVideo 上传本地短视频至群聊
|
// uploadLocalVideo 上传本地短视频至群聊
|
||||||
func (bot *CQBot) uploadLocalVideo(target message.Source, v *LocalVideoElement) (*message.ShortVideoElement, error) {
|
func (bot *CQBot) uploadLocalVideo(target message.Source, v *msg.LocalVideo) (*message.ShortVideoElement, error) {
|
||||||
video, err := os.Open(v.File)
|
video, err := os.Open(v.File)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer func() { _ = video.Close() }()
|
defer func() { _ = video.Close() }()
|
||||||
return bot.Client.UploadShortVideo(target, video, v.thumb, 4)
|
return bot.Client.UploadShortVideo(target, video, v.Thumb)
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeLocalElement(elements []message.IMessageElement) []message.IMessageElement {
|
func removeLocalElement(elements []message.IMessageElement) []message.IMessageElement {
|
||||||
var j int
|
var j int
|
||||||
for i, e := range elements {
|
for i, e := range elements {
|
||||||
switch e.(type) {
|
switch e.(type) {
|
||||||
case *LocalImageElement, *LocalVideoElement:
|
case *msg.LocalImage, *msg.LocalVideo:
|
||||||
case *message.VoiceElement: // 未上传的语音消息, 也删除
|
case *message.VoiceElement: // 未上传的语音消息, 也删除
|
||||||
case nil:
|
case nil:
|
||||||
default:
|
default:
|
||||||
@ -214,7 +231,7 @@ func (bot *CQBot) uploadMedia(target message.Source, elements []message.IMessage
|
|||||||
for i, m := range elements {
|
for i, m := range elements {
|
||||||
p := &elements[i]
|
p := &elements[i]
|
||||||
switch e := m.(type) {
|
switch e := m.(type) {
|
||||||
case *LocalImageElement:
|
case *msg.LocalImage:
|
||||||
w.do(func() {
|
w.do(func() {
|
||||||
m, err := bot.uploadLocalImage(target, e)
|
m, err := bot.uploadLocalImage(target, e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -232,7 +249,7 @@ func (bot *CQBot) uploadMedia(target message.Source, elements []message.IMessage
|
|||||||
*p = m
|
*p = m
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
case *LocalVideoElement:
|
case *msg.LocalVideo:
|
||||||
w.do(func() {
|
w.do(func() {
|
||||||
m, err := bot.uploadLocalVideo(target, e)
|
m, err := bot.uploadLocalVideo(target, e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -258,7 +275,7 @@ func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) (in
|
|||||||
m.Elements = bot.uploadMedia(source, m.Elements)
|
m.Elements = bot.uploadMedia(source, m.Elements)
|
||||||
for _, e := range m.Elements {
|
for _, e := range m.Elements {
|
||||||
switch i := e.(type) {
|
switch i := e.(type) {
|
||||||
case *PokeElement:
|
case *msg.Poke:
|
||||||
if group != nil {
|
if group != nil {
|
||||||
if mem := group.FindMember(i.Target); mem != nil {
|
if mem := group.FindMember(i.Target); mem != nil {
|
||||||
mem.Poke()
|
mem.Poke()
|
||||||
@ -303,7 +320,7 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen
|
|||||||
m.Elements = bot.uploadMedia(source, m.Elements)
|
m.Elements = bot.uploadMedia(source, m.Elements)
|
||||||
for _, e := range m.Elements {
|
for _, e := range m.Elements {
|
||||||
switch i := e.(type) {
|
switch i := e.(type) {
|
||||||
case *PokeElement:
|
case *msg.Poke:
|
||||||
bot.Client.SendFriendPoke(i.Target)
|
bot.Client.SendFriendPoke(i.Target)
|
||||||
return 0
|
return 0
|
||||||
case *message.MusicShareElement:
|
case *message.MusicShareElement:
|
||||||
@ -405,7 +422,7 @@ func (bot *CQBot) SendGuildChannelMessage(guildID, channelID uint64, m *message.
|
|||||||
bot.Client.SendGuildMusicShare(guildID, channelID, i)
|
bot.Client.SendGuildMusicShare(guildID, channelID, i)
|
||||||
return "-1" // todo: fix this
|
return "-1" // todo: fix this
|
||||||
|
|
||||||
case *message.VoiceElement, *PokeElement:
|
case *message.VoiceElement, *msg.Poke:
|
||||||
log.Warnf("警告: 频道暂不支持发送 %v 消息", i.Type().String())
|
log.Warnf("警告: 频道暂不支持发送 %v 消息", i.Type().String())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,9 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Mrs4s/MiraiGo/topic"
|
|
||||||
|
|
||||||
"github.com/Mrs4s/MiraiGo/client"
|
"github.com/Mrs4s/MiraiGo/client"
|
||||||
"github.com/Mrs4s/MiraiGo/message"
|
"github.com/Mrs4s/MiraiGo/message"
|
||||||
|
"github.com/Mrs4s/MiraiGo/topic"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/Mrs4s/go-cqhttp/global"
|
"github.com/Mrs4s/go-cqhttp/global"
|
||||||
|
925
coolq/cqcode.go
925
coolq/cqcode.go
File diff suppressed because it is too large
Load Diff
@ -1,67 +0,0 @@
|
|||||||
package cqcode
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/Mrs4s/MiraiGo/binary"
|
|
||||||
"github.com/Mrs4s/go-cqhttp/global"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Element single message
|
|
||||||
type Element struct {
|
|
||||||
Type string
|
|
||||||
Data []Pair
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pair key value pair
|
|
||||||
type Pair struct {
|
|
||||||
K string
|
|
||||||
V string
|
|
||||||
}
|
|
||||||
|
|
||||||
// CQCode convert element to cqcode
|
|
||||||
func (e *Element) CQCode() string {
|
|
||||||
buf := strings.Builder{}
|
|
||||||
e.WriteCQCodeTo(&buf)
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteCQCodeTo write element's cqcode into sb
|
|
||||||
func (e *Element) WriteCQCodeTo(sb *strings.Builder) {
|
|
||||||
if e.Type == "text" {
|
|
||||||
sb.WriteString(EscapeText(e.Data[0].V)) // must be {"text": value}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sb.WriteString("[CQ:")
|
|
||||||
sb.WriteString(e.Type)
|
|
||||||
for _, data := range e.Data {
|
|
||||||
sb.WriteByte(',')
|
|
||||||
sb.WriteString(data.K)
|
|
||||||
sb.WriteByte('=')
|
|
||||||
sb.WriteString(EscapeValue(data.V))
|
|
||||||
}
|
|
||||||
sb.WriteByte(']')
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON see encoding/json.Marshaler
|
|
||||||
func (e *Element) MarshalJSON() ([]byte, error) {
|
|
||||||
return binary.NewWriterF(func(w *binary.Writer) {
|
|
||||||
buf := (*bytes.Buffer)(w)
|
|
||||||
// fmt.Fprintf(buf, `{"type":"%s","data":{`, e.Type)
|
|
||||||
buf.WriteString(`{"type":"`)
|
|
||||||
buf.WriteString(e.Type)
|
|
||||||
buf.WriteString(`","data":{`)
|
|
||||||
for i, data := range e.Data {
|
|
||||||
if i != 0 {
|
|
||||||
buf.WriteByte(',')
|
|
||||||
}
|
|
||||||
// fmt.Fprintf(buf, `"%s":%q`, data.K, data.V)
|
|
||||||
buf.WriteByte('"')
|
|
||||||
buf.WriteString(data.K)
|
|
||||||
buf.WriteString(`":`)
|
|
||||||
buf.WriteString(global.Quote(data.V))
|
|
||||||
}
|
|
||||||
buf.WriteString(`}}`)
|
|
||||||
}), nil
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
// Package cqcode provides CQCode util functions.
|
|
||||||
package cqcode
|
|
||||||
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
// EscapeText 将字符串raw中部分字符转义
|
|
||||||
//
|
|
||||||
// - & -> &
|
|
||||||
// - [ -> [
|
|
||||||
// - ] -> ]
|
|
||||||
func EscapeText(s string) string {
|
|
||||||
count := strings.Count(s, "&")
|
|
||||||
count += strings.Count(s, "[")
|
|
||||||
count += strings.Count(s, "]")
|
|
||||||
if count == 0 {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply replacements to buffer.
|
|
||||||
var b strings.Builder
|
|
||||||
b.Grow(len(s) + count*4)
|
|
||||||
start := 0
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
j := start
|
|
||||||
for index, r := range s[start:] {
|
|
||||||
if r == '&' || r == '[' || r == ']' {
|
|
||||||
j += index
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b.WriteString(s[start:j])
|
|
||||||
switch s[j] {
|
|
||||||
case '&':
|
|
||||||
b.WriteString("&")
|
|
||||||
case '[':
|
|
||||||
b.WriteString("[")
|
|
||||||
case ']':
|
|
||||||
b.WriteString("]")
|
|
||||||
}
|
|
||||||
start = j + 1
|
|
||||||
}
|
|
||||||
b.WriteString(s[start:])
|
|
||||||
return b.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// EscapeValue 将字符串value中部分字符转义
|
|
||||||
//
|
|
||||||
// - , -> ,
|
|
||||||
// - & -> &
|
|
||||||
// - [ -> [
|
|
||||||
// - ] -> ]
|
|
||||||
func EscapeValue(value string) string {
|
|
||||||
ret := EscapeText(value)
|
|
||||||
return strings.ReplaceAll(ret, ",", ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnescapeText 将字符串content中部分字符反转义
|
|
||||||
//
|
|
||||||
// - & -> &
|
|
||||||
// - [ -> [
|
|
||||||
// - ] -> ]
|
|
||||||
func UnescapeText(content string) string {
|
|
||||||
ret := content
|
|
||||||
ret = strings.ReplaceAll(ret, "[", "[")
|
|
||||||
ret = strings.ReplaceAll(ret, "]", "]")
|
|
||||||
ret = strings.ReplaceAll(ret, "&", "&")
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnescapeValue 将字符串content中部分字符反转义
|
|
||||||
//
|
|
||||||
// - , -> ,
|
|
||||||
// - & -> &
|
|
||||||
// - [ -> [
|
|
||||||
// - ] -> ]
|
|
||||||
func UnescapeValue(content string) string {
|
|
||||||
ret := strings.ReplaceAll(content, ",", ",")
|
|
||||||
return UnescapeText(ret)
|
|
||||||
}
|
|
@ -21,7 +21,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ToFormattedMessage 将给定[]message.IMessageElement转换为通过coolq.SetMessageFormat所定义的消息上报格式
|
// ToFormattedMessage 将给定[]message.IMessageElement转换为通过coolq.SetMessageFormat所定义的消息上报格式
|
||||||
func ToFormattedMessage(e []message.IMessageElement, source message.Source) (r interface{}) {
|
func ToFormattedMessage(e []message.IMessageElement, source message.Source) (r any) {
|
||||||
if base.PostFormat == "string" {
|
if base.PostFormat == "string" {
|
||||||
r = toStringMessage(e, source)
|
r = toStringMessage(e, source)
|
||||||
} else if base.PostFormat == "array" {
|
} else if base.PostFormat == "array" {
|
||||||
@ -144,7 +144,10 @@ func (bot *CQBot) tempMessageEvent(c *client.QQClient, e *client.TempMessageEven
|
|||||||
PrimaryID: e.Session.Sender,
|
PrimaryID: e.Session.Sender,
|
||||||
}
|
}
|
||||||
cqm := toStringMessage(m.Elements, source)
|
cqm := toStringMessage(m.Elements, source)
|
||||||
bot.tempSessionCache.Store(m.Sender.Uin, e.Session)
|
if base.AllowTempSession {
|
||||||
|
bot.tempSessionCache.Store(m.Sender.Uin, e.Session)
|
||||||
|
}
|
||||||
|
|
||||||
id := m.Id
|
id := m.Id
|
||||||
// todo(Mrs4s)
|
// todo(Mrs4s)
|
||||||
// if bot.db != nil { // nolint
|
// if bot.db != nil { // nolint
|
||||||
@ -472,6 +475,9 @@ func (bot *CQBot) offlineFileEvent(c *client.QQClient, e *client.OfflineFileEven
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (bot *CQBot) joinGroupEvent(c *client.QQClient, group *client.GroupInfo) {
|
func (bot *CQBot) joinGroupEvent(c *client.QQClient, group *client.GroupInfo) {
|
||||||
|
if group == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
log.Infof("Bot进入了群 %v.", formatGroupName(group))
|
log.Infof("Bot进入了群 %v.", formatGroupName(group))
|
||||||
bot.dispatch(bot.groupIncrease(group.Code, 0, c.Uin))
|
bot.dispatch(bot.groupIncrease(group.Code, 0, c.Uin))
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ func (r *reader) arrayMsg() []global.MSG {
|
|||||||
return msgs
|
return msgs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *reader) obj() interface{} {
|
func (r *reader) obj() any {
|
||||||
switch coder := r.coder(); coder {
|
switch coder := r.coder(); coder {
|
||||||
case coderNil:
|
case coderNil:
|
||||||
return nil
|
return nil
|
||||||
|
@ -96,7 +96,7 @@ func (w *writer) arrayMsg(a []global.MSG) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *writer) obj(o interface{}) {
|
func (w *writer) obj(o any) {
|
||||||
switch x := o.(type) {
|
switch x := o.(type) {
|
||||||
case nil:
|
case nil:
|
||||||
w.nil()
|
w.nil()
|
||||||
|
@ -11,10 +11,12 @@ import (
|
|||||||
|
|
||||||
sql "github.com/FloatTech/sqlite"
|
sql "github.com/FloatTech/sqlite"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
"github.com/Mrs4s/MiraiGo/binary"
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
"github.com/Mrs4s/MiraiGo/utils"
|
"github.com/Mrs4s/MiraiGo/utils"
|
||||||
|
|
||||||
"github.com/Mrs4s/go-cqhttp/db"
|
"github.com/Mrs4s/go-cqhttp/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -26,8 +28,8 @@ type database struct {
|
|||||||
|
|
||||||
// config mongodb 相关配置
|
// config mongodb 相关配置
|
||||||
type config struct {
|
type config struct {
|
||||||
Enable bool `yaml:"enable"`
|
Enable bool `yaml:"enable"`
|
||||||
CacheTTL time.Duration `yaml:"cachettl"`
|
CacheTTL string `yaml:"cachettl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -38,7 +40,11 @@ func init() {
|
|||||||
if !conf.Enable {
|
if !conf.Enable {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return &database{db: new(sql.Sqlite), ttl: conf.CacheTTL}
|
duration, err := time.ParseDuration(conf.CacheTTL)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("illegal ttl config: %v", err)
|
||||||
|
}
|
||||||
|
return &database{db: new(sql.Sqlite), ttl: duration}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
注意: 与客户端建立连接的握手事件**不会**经过事件过滤器
|
注意: 与客户端建立连接的握手事件**不会**经过事件过滤器
|
||||||
|
|
||||||
|
> 注意, 最新文档已经移动到 [go-cqhttp-docs](https://github.com/ishkong/go-cqhttp-docs), 当前文档只做兼容性保留, 所以内容可能有不足.
|
||||||
|
|
||||||
## 示例
|
## 示例
|
||||||
|
|
||||||
这节首先给出一些示例,演示过滤器的基本用法,下一节将给出具体语法说明。
|
这节首先给出一些示例,演示过滤器的基本用法,下一节将给出具体语法说明。
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
# 常见问题
|
# 常见问题
|
||||||
|
|
||||||
|
> 注意, 最新文档已经移动到 [go-cqhttp-docs](https://github.com/ishkong/go-cqhttp-docs), 当前文档只做兼容性保留, 所以内容可能有不足.
|
||||||
|
|
||||||
### Q: 为什么挂一段时间后就会出现 `消息发送失败,账号可能被风控`?
|
### Q: 为什么挂一段时间后就会出现 `消息发送失败,账号可能被风控`?
|
||||||
|
|
||||||
### A: 如果你刚开始使用 go-cqhttp 建议挂机3-7天,即可解除风控
|
### A: 如果你刚开始使用 go-cqhttp 建议挂机3-7天,即可解除风控
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
# 配置
|
# 配置
|
||||||
|
|
||||||
|
> 注意, 最新文档已经移动到 [go-cqhttp-docs](https://github.com/ishkong/go-cqhttp-docs), 当前文档只做兼容性保留, 所以内容可能有不足.
|
||||||
|
|
||||||
go-cqhttp 包含 `config.yml` 和 `device.json` 两个配置文件, 其中 `config.yml` 为运行配置 `device.json` 为虚拟设备信息.
|
go-cqhttp 包含 `config.yml` 和 `device.json` 两个配置文件, 其中 `config.yml` 为运行配置 `device.json` 为虚拟设备信息.
|
||||||
|
|
||||||
## 配置信息
|
## 配置信息
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
# 拓展API
|
# 拓展API
|
||||||
|
|
||||||
由于部分 api 原版 CQHTTP 并未实现,go-cqhttp 修改并增加了一些拓展 api .
|
由于部分 api 原版 CQHTTP 并未实现,go-cqhttp 修改并增加了一些拓展 api
|
||||||
|
|
||||||
|
> 注意, 最新文档已经移动到 [go-cqhttp-docs](https://github.com/ishkong/go-cqhttp-docs), 当前文档只做兼容性保留, 所以内容可能有不足..
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>目录</summary>
|
<summary>目录</summary>
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
QQ频道相关功能的事件以及API
|
QQ频道相关功能的事件以及API
|
||||||
|
|
||||||
|
> 注意, 最新文档已经移动到 [go-cqhttp-docs](https://github.com/ishkong/go-cqhttp-docs), 当前文档只做兼容性保留, 所以内容可能有不足.
|
||||||
|
|
||||||
## 命名说明
|
## 命名说明
|
||||||
|
|
||||||
API以及字段相关命名均为参考QQ官方命名或相似产品命名规则, 由于QQ频道的账号系统独立于QQ本体, 所以各个 `ID` 并不能和QQ通用.也无法通过 `tiny_id` 获取到 `QQ号`
|
API以及字段相关命名均为参考QQ官方命名或相似产品命名规则, 由于QQ频道的账号系统独立于QQ本体, 所以各个 `ID` 并不能和QQ通用.也无法通过 `tiny_id` 获取到 `QQ号`
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
欢迎来到 go-cqhttp 文档 目前还在咕
|
欢迎来到 go-cqhttp 文档 目前还在咕
|
||||||
|
|
||||||
|
> 注意, 最新文档已经移动到 [go-cqhttp-docs](https://github.com/ishkong/go-cqhttp-docs), 当前文档只做兼容性保留, 所以内容可能有不足.
|
||||||
|
|
||||||
# 基础教程
|
# 基础教程
|
||||||
## 下载
|
## 下载
|
||||||
从[release](https://github.com/Mrs4s/go-cqhttp/releases)界面下载最新版本的go-cqhttp
|
从[release](https://github.com/Mrs4s/go-cqhttp/releases)界面下载最新版本的go-cqhttp
|
||||||
|
@ -33,9 +33,15 @@ func EncoderSilk(data []byte) ([]byte, error) {
|
|||||||
// EncodeMP4 将给定视频文件编码为MP4
|
// EncodeMP4 将给定视频文件编码为MP4
|
||||||
func EncodeMP4(src string, dst string) error { // -y 覆盖文件
|
func EncodeMP4(src string, dst string) error { // -y 覆盖文件
|
||||||
cmd1 := exec.Command("ffmpeg", "-i", src, "-y", "-c", "copy", "-map", "0", dst)
|
cmd1 := exec.Command("ffmpeg", "-i", src, "-y", "-c", "copy", "-map", "0", dst)
|
||||||
|
if errors.Is(cmd1.Err, exec.ErrDot) {
|
||||||
|
cmd1.Err = nil
|
||||||
|
}
|
||||||
err := cmd1.Run()
|
err := cmd1.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmd2 := exec.Command("ffmpeg", "-i", src, "-y", "-c:v", "h264", "-c:a", "mp3", dst)
|
cmd2 := exec.Command("ffmpeg", "-i", src, "-y", "-c:v", "h264", "-c:a", "mp3", dst)
|
||||||
|
if errors.Is(cmd2.Err, exec.ErrDot) {
|
||||||
|
cmd2.Err = nil
|
||||||
|
}
|
||||||
return errors.Wrap(cmd2.Run(), "convert mp4 failed")
|
return errors.Wrap(cmd2.Run(), "convert mp4 failed")
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
@ -44,5 +50,8 @@ func EncodeMP4(src string, dst string) error { // -y 覆盖文件
|
|||||||
// ExtractCover 获取给定视频文件的Cover
|
// ExtractCover 获取给定视频文件的Cover
|
||||||
func ExtractCover(src string, target string) error {
|
func ExtractCover(src string, target string) error {
|
||||||
cmd := exec.Command("ffmpeg", "-i", src, "-y", "-ss", "0", "-frames:v", "1", target)
|
cmd := exec.Command("ffmpeg", "-i", src, "-y", "-ss", "0", "-frames:v", "1", target)
|
||||||
|
if errors.Is(cmd.Err, exec.ErrDot) {
|
||||||
|
cmd.Err = nil
|
||||||
|
}
|
||||||
return errors.Wrap(cmd.Run(), "extract video cover failed")
|
return errors.Wrap(cmd.Run(), "extract video cover failed")
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,8 @@ const (
|
|||||||
VoicePath = "data/voices"
|
VoicePath = "data/voices"
|
||||||
// VideoPath go-cqhttp使用的视频缓存目录
|
// VideoPath go-cqhttp使用的视频缓存目录
|
||||||
VideoPath = "data/videos"
|
VideoPath = "data/videos"
|
||||||
|
// VersionsPath go-cqhttp使用的版本信息目录
|
||||||
|
VersionsPath = "data/versions"
|
||||||
// CachePath go-cqhttp使用的缓存目录
|
// CachePath go-cqhttp使用的缓存目录
|
||||||
CachePath = "data/cache"
|
CachePath = "data/cache"
|
||||||
// DumpsPath go-cqhttp使用错误转储目录
|
// DumpsPath go-cqhttp使用错误转储目录
|
||||||
@ -88,6 +90,7 @@ func FindFile(file, cache, p string) (data []byte, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return os.ReadFile(cacheFile)
|
||||||
case strings.HasPrefix(file, "base64"):
|
case strings.HasPrefix(file, "base64"):
|
||||||
data, err = base64.StdEncoding.DecodeString(strings.TrimPrefix(file, "base64://"))
|
data, err = base64.StdEncoding.DecodeString(strings.TrimPrefix(file, "base64://"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -108,7 +108,7 @@ func (hook *LocalHook) SetPath(path string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewLocalHook 初始化本地日志钩子实现
|
// NewLocalHook 初始化本地日志钩子实现
|
||||||
func NewLocalHook(args interface{}, consoleFormatter, fileFormatter logrus.Formatter, levels ...logrus.Level) *LocalHook {
|
func NewLocalHook(args any, consoleFormatter, fileFormatter logrus.Formatter, levels ...logrus.Level) *LocalHook {
|
||||||
hook := &LocalHook{
|
hook := &LocalHook{
|
||||||
lock: new(sync.Mutex),
|
lock: new(sync.Mutex),
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// MSG 消息Map
|
// MSG 消息Map
|
||||||
type MSG = map[string]interface{}
|
type MSG = map[string]any
|
||||||
|
|
||||||
// VersionNameCompare 检查版本名是否需要更新, 仅适用于 go-cqhttp 的版本命名规则
|
// VersionNameCompare 检查版本名是否需要更新, 仅适用于 go-cqhttp 的版本命名规则
|
||||||
//
|
//
|
||||||
|
146
global/quote.go
146
global/quote.go
@ -1,146 +0,0 @@
|
|||||||
package global
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
lowerhex = "0123456789abcdef"
|
|
||||||
upperhex = "0123456789ABCDEF"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Quote returns a double-quoted Go string literal representing s. The
|
|
||||||
// returned string uses Go escape sequences (\t, \n, \xFF, \u0100) for
|
|
||||||
// control characters and non-printable characters as defined by
|
|
||||||
// IsPrint.
|
|
||||||
func Quote(s string) string {
|
|
||||||
return quoteWith(s, '"', false, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func quoteWith(s string, quote byte, asciiOnly, graphicOnly bool) string {
|
|
||||||
return string(appendQuotedWith(make([]byte, 0, 3*len(s)/2), s, quote, asciiOnly, graphicOnly))
|
|
||||||
}
|
|
||||||
|
|
||||||
func appendQuotedWith(buf []byte, s string, quote byte, asciiOnly, graphicOnly bool) []byte {
|
|
||||||
// Often called with big strings, so preallocate. If there's quoting,
|
|
||||||
// this is conservative but still helps a lot.
|
|
||||||
if cap(buf)-len(buf) < len(s) {
|
|
||||||
nBuf := make([]byte, len(buf), len(buf)+1+len(s)+1)
|
|
||||||
copy(nBuf, buf)
|
|
||||||
buf = nBuf
|
|
||||||
}
|
|
||||||
buf = append(buf, quote)
|
|
||||||
for width := 0; len(s) > 0; s = s[width:] {
|
|
||||||
r := rune(s[0])
|
|
||||||
width = 1
|
|
||||||
if r >= utf8.RuneSelf {
|
|
||||||
r, width = utf8.DecodeRuneInString(s)
|
|
||||||
}
|
|
||||||
if width == 1 && r == utf8.RuneError {
|
|
||||||
buf = append(buf, `\x`...)
|
|
||||||
buf = append(buf, lowerhex[s[0]>>4])
|
|
||||||
buf = append(buf, lowerhex[s[0]&0xF])
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
buf = appendEscapedRune(buf, r, quote, asciiOnly, graphicOnly)
|
|
||||||
}
|
|
||||||
buf = append(buf, quote)
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
func appendEscapedRune(buf []byte, r rune, quote byte, asciiOnly, graphicOnly bool) []byte {
|
|
||||||
var runeTmp [utf8.UTFMax]byte
|
|
||||||
if r == rune(quote) || r == '\\' { // always backslashed
|
|
||||||
buf = append(buf, '\\')
|
|
||||||
buf = append(buf, byte(r))
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
if asciiOnly {
|
|
||||||
if r < utf8.RuneSelf && strconv.IsPrint(r) {
|
|
||||||
buf = append(buf, byte(r))
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
} else if strconv.IsPrint(r) || graphicOnly && isInGraphicList(r) {
|
|
||||||
n := utf8.EncodeRune(runeTmp[:], r)
|
|
||||||
buf = append(buf, runeTmp[:n]...)
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
switch r {
|
|
||||||
case '\a':
|
|
||||||
buf = append(buf, `\a`...)
|
|
||||||
case '\b':
|
|
||||||
buf = append(buf, `\b`...)
|
|
||||||
case '\f':
|
|
||||||
buf = append(buf, `\f`...)
|
|
||||||
case '\n':
|
|
||||||
buf = append(buf, `\n`...)
|
|
||||||
case '\r':
|
|
||||||
buf = append(buf, `\r`...)
|
|
||||||
case '\t':
|
|
||||||
buf = append(buf, `\t`...)
|
|
||||||
case '\v':
|
|
||||||
buf = append(buf, `\v`...)
|
|
||||||
default:
|
|
||||||
switch {
|
|
||||||
case !utf8.ValidRune(r):
|
|
||||||
r = 0xFFFD
|
|
||||||
fallthrough
|
|
||||||
case r < 0x10000:
|
|
||||||
buf = append(buf, `\u`...)
|
|
||||||
for s := 12; s >= 0; s -= 4 {
|
|
||||||
buf = append(buf, lowerhex[r>>uint(s)&0xF])
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
buf = append(buf, `\U`...)
|
|
||||||
for s := 28; s >= 0; s -= 4 {
|
|
||||||
buf = append(buf, lowerhex[r>>uint(s)&0xF])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|
||||||
func isInGraphicList(r rune) bool {
|
|
||||||
// We know r must fit in 16 bits - see makeisprint.go.
|
|
||||||
if r > 0xFFFF {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
rr := uint16(r)
|
|
||||||
i := bsearch16(isGraphic, rr)
|
|
||||||
return i < len(isGraphic) && rr == isGraphic[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
// bsearch16 returns the smallest i such that a[i] >= x.
|
|
||||||
// If there is no such i, bsearch16 returns len(a).
|
|
||||||
func bsearch16(a []uint16, x uint16) int {
|
|
||||||
i, j := 0, len(a)
|
|
||||||
for i < j {
|
|
||||||
h := i + (j-i)>>1
|
|
||||||
if a[h] < x {
|
|
||||||
i = h + 1
|
|
||||||
} else {
|
|
||||||
j = h
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
// isGraphic lists the graphic runes not matched by IsPrint.
|
|
||||||
var isGraphic = []uint16{
|
|
||||||
0x00a0,
|
|
||||||
0x1680,
|
|
||||||
0x2000,
|
|
||||||
0x2001,
|
|
||||||
0x2002,
|
|
||||||
0x2003,
|
|
||||||
0x2004,
|
|
||||||
0x2005,
|
|
||||||
0x2006,
|
|
||||||
0x2007,
|
|
||||||
0x2008,
|
|
||||||
0x2009,
|
|
||||||
0x200a,
|
|
||||||
0x202f,
|
|
||||||
0x205f,
|
|
||||||
0x3000,
|
|
||||||
}
|
|
@ -14,9 +14,9 @@ import (
|
|||||||
func SetupMainSignalHandler() <-chan struct{} {
|
func SetupMainSignalHandler() <-chan struct{} {
|
||||||
mainOnce.Do(func() {
|
mainOnce.Do(func() {
|
||||||
mainStopCh = make(chan struct{})
|
mainStopCh = make(chan struct{})
|
||||||
mc := make(chan os.Signal, 3)
|
mc := make(chan os.Signal, 4)
|
||||||
closeOnce := sync.Once{}
|
closeOnce := sync.Once{}
|
||||||
signal.Notify(mc, os.Interrupt, syscall.SIGTERM, syscall.SIGUSR1)
|
signal.Notify(mc, os.Interrupt, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGUSR1)
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
switch <-mc {
|
switch <-mc {
|
||||||
@ -24,7 +24,7 @@ func SetupMainSignalHandler() <-chan struct{} {
|
|||||||
closeOnce.Do(func() {
|
closeOnce.Do(func() {
|
||||||
close(mainStopCh)
|
close(mainStopCh)
|
||||||
})
|
})
|
||||||
case syscall.SIGUSR1:
|
case syscall.SIGQUIT, syscall.SIGUSR1:
|
||||||
dumpStack()
|
dumpStack()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
// Package terminal 包含用于检测在windows下是否通过双击运行go-cqhttp的函数
|
// Package terminal 包含用于检测在windows下是否通过双击运行go-cqhttp, 禁用快速编辑, 启用VT100的函数
|
||||||
package terminal
|
package terminal
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
//go:build !windows
|
//go:build !windows
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
package terminal
|
package terminal
|
||||||
|
|
||||||
// RunningByDoubleClick 检查是否通过双击直接运行,非Windows系统永远返回false
|
// RunningByDoubleClick 检查是否通过双击直接运行,非Windows系统永远返回false
|
||||||
func RunningByDoubleClick() bool {
|
func RunningByDoubleClick() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
//go:build windows
|
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package terminal
|
package terminal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
13
global/terminal/quick_edit.go
Normal file
13
global/terminal/quick_edit.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package terminal
|
||||||
|
|
||||||
|
// RestoreInputMode 还原输入模式,非Windows系统永远返回nil
|
||||||
|
func RestoreInputMode() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableQuickEdit 禁用快速编辑,非Windows系统永远返回nil
|
||||||
|
func DisableQuickEdit() error {
|
||||||
|
return nil
|
||||||
|
}
|
44
global/terminal/quick_edit_windows.go
Normal file
44
global/terminal/quick_edit_windows.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package terminal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
var inputmode uint32
|
||||||
|
|
||||||
|
// RestoreInputMode 还原输入模式
|
||||||
|
func RestoreInputMode() error {
|
||||||
|
if inputmode == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
stdin := windows.Handle(os.Stdin.Fd())
|
||||||
|
return windows.SetConsoleMode(stdin, inputmode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableQuickEdit 禁用快速编辑
|
||||||
|
func DisableQuickEdit() error {
|
||||||
|
stdin := windows.Handle(os.Stdin.Fd())
|
||||||
|
|
||||||
|
var mode uint32
|
||||||
|
err := windows.GetConsoleMode(stdin, &mode)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
inputmode = mode
|
||||||
|
|
||||||
|
mode &^= windows.ENABLE_QUICK_EDIT_MODE // 禁用快速编辑模式
|
||||||
|
mode |= windows.ENABLE_EXTENDED_FLAGS // 启用扩展标志
|
||||||
|
|
||||||
|
mode &^= windows.ENABLE_MOUSE_INPUT // 禁用鼠标输入
|
||||||
|
mode |= windows.ENABLE_PROCESSED_INPUT // 启用控制输入
|
||||||
|
|
||||||
|
mode &^= windows.ENABLE_INSERT_MODE // 禁用插入模式
|
||||||
|
mode |= windows.ENABLE_ECHO_INPUT | windows.ENABLE_LINE_INPUT // 启用输入回显&逐行输入
|
||||||
|
|
||||||
|
mode &^= windows.ENABLE_WINDOW_INPUT // 禁用窗口输入
|
||||||
|
mode &^= windows.ENABLE_VIRTUAL_TERMINAL_INPUT // 禁用虚拟终端输入
|
||||||
|
|
||||||
|
return windows.SetConsoleMode(stdin, mode)
|
||||||
|
}
|
15
global/terminal/title.go
Normal file
15
global/terminal/title.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package terminal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Mrs4s/go-cqhttp/internal/base"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetTitle 设置标题为 go-cqhttp `版本` `版权`
|
||||||
|
func SetTitle() {
|
||||||
|
fmt.Printf("\033]0;go-cqhttp "+base.Version+" © 2020 - %d Mrs4s"+"\007", time.Now().Year())
|
||||||
|
}
|
29
global/terminal/title_windows.go
Normal file
29
global/terminal/title_windows.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package terminal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
|
||||||
|
"github.com/Mrs4s/go-cqhttp/internal/base"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setConsoleTitle(title string) error {
|
||||||
|
p0, err := syscall.UTF16PtrFromString(title)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r1, _, err := windows.NewLazySystemDLL("kernel32.dll").NewProc("SetConsoleTitleW").Call(uintptr(unsafe.Pointer(p0)))
|
||||||
|
if r1 == 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTitle 设置标题为 go-cqhttp `版本` `版权`
|
||||||
|
func SetTitle() {
|
||||||
|
_ = setConsoleTitle(fmt.Sprintf("go-cqhttp "+base.Version+" © 2020 - %d Mrs4s", time.Now().Year()))
|
||||||
|
}
|
8
global/terminal/vt100.go
Normal file
8
global/terminal/vt100.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package terminal
|
||||||
|
|
||||||
|
// EnableVT100 启用颜色、控制字符,非Windows系统永远返回nil
|
||||||
|
func EnableVT100() error {
|
||||||
|
return nil
|
||||||
|
}
|
23
global/terminal/vt100_windows.go
Normal file
23
global/terminal/vt100_windows.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package terminal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EnableVT100 启用颜色、控制字符
|
||||||
|
func EnableVT100() error {
|
||||||
|
stdout := windows.Handle(os.Stdout.Fd())
|
||||||
|
|
||||||
|
var mode uint32
|
||||||
|
err := windows.GetConsoleMode(stdout, &mode)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING // 启用虚拟终端处理
|
||||||
|
mode |= windows.ENABLE_PROCESSED_OUTPUT // 启用处理后的输出
|
||||||
|
|
||||||
|
return windows.SetConsoleMode(stdout, mode)
|
||||||
|
}
|
15
go.mod
15
go.mod
@ -1,11 +1,11 @@
|
|||||||
module github.com/Mrs4s/go-cqhttp
|
module github.com/Mrs4s/go-cqhttp
|
||||||
|
|
||||||
go 1.19
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/FloatTech/sqlite v1.5.7
|
github.com/FloatTech/sqlite v1.5.7
|
||||||
github.com/Microsoft/go-winio v0.6.0
|
github.com/Microsoft/go-winio v0.6.0
|
||||||
github.com/Mrs4s/MiraiGo v0.0.0-20221202060717-4658474c60dd
|
github.com/Mrs4s/MiraiGo v0.0.0-20230401072048-f8d9841755b5
|
||||||
github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e
|
github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e
|
||||||
github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc
|
github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc
|
||||||
github.com/fumiama/go-base16384 v1.6.1
|
github.com/fumiama/go-base16384 v1.6.1
|
||||||
@ -21,16 +21,17 @@ require (
|
|||||||
github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60
|
github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60
|
||||||
go.mongodb.org/mongo-driver v1.11.0
|
go.mongodb.org/mongo-driver v1.11.0
|
||||||
golang.org/x/crypto v0.3.0
|
golang.org/x/crypto v0.3.0
|
||||||
|
golang.org/x/image v0.5.0
|
||||||
golang.org/x/sys v0.2.0
|
golang.org/x/sys v0.2.0
|
||||||
golang.org/x/term v0.2.0
|
golang.org/x/term v0.2.0
|
||||||
golang.org/x/time v0.2.0
|
golang.org/x/time v0.2.0
|
||||||
gopkg.ilharper.com/x/isatty v1.1.0
|
gopkg.ilharper.com/x/isatty v1.1.1
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b // indirect
|
github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b // indirect
|
||||||
github.com/RomiChan/protobuf v0.0.0-20220624030127-3310cba9dbc0 // indirect
|
github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/fumiama/imgsz v0.0.2 // indirect
|
github.com/fumiama/imgsz v0.0.2 // indirect
|
||||||
github.com/golang/snappy v0.0.4 // indirect
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
@ -43,7 +44,7 @@ require (
|
|||||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.15 // indirect
|
github.com/pierrec/lz4/v4 v4.1.15 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/tidwall/match v1.1.1 // indirect
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
github.com/tidwall/pretty v1.2.0 // indirect
|
github.com/tidwall/pretty v1.2.0 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
@ -52,7 +53,7 @@ require (
|
|||||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||||
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect
|
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect
|
||||||
golang.org/x/text v0.4.0 // indirect
|
golang.org/x/text v0.7.0 // indirect
|
||||||
golang.org/x/tools v0.1.12 // indirect
|
golang.org/x/tools v0.1.12 // indirect
|
||||||
lukechampine.com/uint128 v1.2.0 // indirect
|
lukechampine.com/uint128 v1.2.0 // indirect
|
||||||
modernc.org/cc/v3 v3.40.0 // indirect
|
modernc.org/cc/v3 v3.40.0 // indirect
|
||||||
@ -65,5 +66,3 @@ require (
|
|||||||
modernc.org/strutil v1.1.3 // indirect
|
modernc.org/strutil v1.1.3 // indirect
|
||||||
modernc.org/token v1.0.1 // indirect
|
modernc.org/token v1.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/remyoudompheng/bigfft => github.com/fumiama/bigfft v0.0.0-20211011143303-6e0bfa3c836b
|
|
||||||
|
38
go.sum
38
go.sum
@ -4,10 +4,10 @@ github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJG
|
|||||||
github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs=
|
github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs=
|
||||||
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
|
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
|
||||||
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
|
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
|
||||||
github.com/Mrs4s/MiraiGo v0.0.0-20221202060717-4658474c60dd h1:rzAbPc++5CJ1VZDjq/eORXOWMMGsDN3DMAPMXfI7Fvs=
|
github.com/Mrs4s/MiraiGo v0.0.0-20230401072048-f8d9841755b5 h1:E4fIQ0l/LNZK44NjdViRb/hx4cIeHXyQFPzzkx7cjVE=
|
||||||
github.com/Mrs4s/MiraiGo v0.0.0-20221202060717-4658474c60dd/go.mod h1:lecSP26qedhinCceWn1x02dLDxGotH5nTFlpIMilmVM=
|
github.com/Mrs4s/MiraiGo v0.0.0-20230401072048-f8d9841755b5/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0=
|
||||||
github.com/RomiChan/protobuf v0.0.0-20220624030127-3310cba9dbc0 h1:GEwcB4dL9vc4veW1fLNt0Fby3wspVflAn5v9/HbUwDM=
|
github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d h1:/Xuj3fIiMY2ls1TwvPKmaqQrtJsPY+c9s+0lOScVHd8=
|
||||||
github.com/RomiChan/protobuf v0.0.0-20220624030127-3310cba9dbc0/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA=
|
github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA=
|
||||||
github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA=
|
github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA=
|
||||||
github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e/go.mod h1:vD7Ra3Q9onRtojoY5sMCLQ7JBgjUsrXDnDKyFxqpf9w=
|
github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e/go.mod h1:vD7Ra3Q9onRtojoY5sMCLQ7JBgjUsrXDnDKyFxqpf9w=
|
||||||
github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0=
|
github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0=
|
||||||
@ -19,8 +19,6 @@ github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4
|
|||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/fumiama/bigfft v0.0.0-20211011143303-6e0bfa3c836b h1:Zt3pFQditAdWTHCOVkiloc9ZauBoWrb37guFV4iIRvE=
|
|
||||||
github.com/fumiama/bigfft v0.0.0-20211011143303-6e0bfa3c836b/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
|
||||||
github.com/fumiama/go-base16384 v1.6.1 h1:4yb4JgmBJDnQtq3XGXXdLrVwEnRpjhMUt4eAcsNeA30=
|
github.com/fumiama/go-base16384 v1.6.1 h1:4yb4JgmBJDnQtq3XGXXdLrVwEnRpjhMUt4eAcsNeA30=
|
||||||
github.com/fumiama/go-base16384 v1.6.1/go.mod h1:OEn+947GV5gsbTAnyuUW/SrfxJYUdYupSIQXOuGOcXM=
|
github.com/fumiama/go-base16384 v1.6.1/go.mod h1:OEn+947GV5gsbTAnyuUW/SrfxJYUdYupSIQXOuGOcXM=
|
||||||
github.com/fumiama/go-hide-param v0.1.4 h1:y7TRTzZMdCH9GOXnIzU3B+1BSkcmvejVGmGsz4t0DGU=
|
github.com/fumiama/go-hide-param v0.1.4 h1:y7TRTzZMdCH9GOXnIzU3B+1BSkcmvejVGmGsz4t0DGU=
|
||||||
@ -52,8 +50,8 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU
|
|||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||||
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
||||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
@ -86,7 +84,9 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
|
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
|
||||||
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
|
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
|
||||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||||
@ -120,23 +120,32 @@ github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCO
|
|||||||
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
|
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
|
||||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
|
||||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
go.mongodb.org/mongo-driver v1.11.0 h1:FZKhBSTydeuffHj9CBjXlR8vQLee1cQyTWYPA6/tqiE=
|
go.mongodb.org/mongo-driver v1.11.0 h1:FZKhBSTydeuffHj9CBjXlR8vQLee1cQyTWYPA6/tqiE=
|
||||||
go.mongodb.org/mongo-driver v1.11.0/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8=
|
go.mongodb.org/mongo-driver v1.11.0/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
|
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
|
||||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||||
|
golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI=
|
||||||
|
golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
|
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc=
|
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc=
|
||||||
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@ -154,11 +163,14 @@ golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
|
golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
|
||||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@ -166,13 +178,15 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/time v0.2.0 h1:52I/1L54xyEQAYdtcSuxtiT84KGYTBGXwayxmIpNJhE=
|
golang.org/x/time v0.2.0 h1:52I/1L54xyEQAYdtcSuxtiT84KGYTBGXwayxmIpNJhE=
|
||||||
golang.org/x/time v0.2.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.2.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@ -182,8 +196,8 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ
|
|||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
gopkg.ilharper.com/x/isatty v1.1.0 h1:slOK6hP9/y9mJWyCInMwnT432NExfWyYV2SsebdYOCY=
|
gopkg.ilharper.com/x/isatty v1.1.1 h1:RAg32Pxq/nIK4AVtdm9RBqxsxZZX1uRKRSS21E5SHMk=
|
||||||
gopkg.ilharper.com/x/isatty v1.1.0/go.mod h1:ofpv77Td5qQO6R1dmDd3oNt8TZdRo+l5gYAMxopRyS0=
|
gopkg.ilharper.com/x/isatty v1.1.1/go.mod h1:ofpv77Td5qQO6R1dmDd3oNt8TZdRo+l5gYAMxopRyS0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
@ -23,19 +23,22 @@ var (
|
|||||||
|
|
||||||
// config file flags
|
// config file flags
|
||||||
var (
|
var (
|
||||||
Debug bool // 是否开启 debug 模式
|
Debug bool // 是否开启 debug 模式
|
||||||
RemoveReplyAt bool // 是否删除reply后的at
|
RemoveReplyAt bool // 是否删除reply后的at
|
||||||
ExtraReplyData bool // 是否上报额外reply信息
|
ExtraReplyData bool // 是否上报额外reply信息
|
||||||
IgnoreInvalidCQCode bool // 是否忽略无效CQ码
|
IgnoreInvalidCQCode bool // 是否忽略无效CQ码
|
||||||
SplitURL bool // 是否分割URL
|
SplitURL bool // 是否分割URL
|
||||||
ForceFragmented bool // 是否启用强制分片
|
ForceFragmented bool // 是否启用强制分片
|
||||||
SkipMimeScan bool // 是否跳过Mime扫描
|
SkipMimeScan bool // 是否跳过Mime扫描
|
||||||
ReportSelfMessage bool // 是否上报自身消息
|
ConvertWebpImage bool // 是否转换Webp图片
|
||||||
UseSSOAddress bool // 是否使用服务器下发的新地址进行重连
|
ReportSelfMessage bool // 是否上报自身消息
|
||||||
LogForceNew bool // 是否在每次启动时强制创建全新的文件储存日志
|
UseSSOAddress bool // 是否使用服务器下发的新地址进行重连
|
||||||
LogColorful bool // 是否启用日志颜色
|
LogForceNew bool // 是否在每次启动时强制创建全新的文件储存日志
|
||||||
FastStart bool // 是否为快速启动
|
LogColorful bool // 是否启用日志颜色
|
||||||
AllowTempSession bool // 是否允许发送临时会话信息
|
FastStart bool // 是否为快速启动
|
||||||
|
AllowTempSession bool // 是否允许发送临时会话信息
|
||||||
|
UpdateProtocol bool // 是否更新协议
|
||||||
|
SignServerOverwrite string // 使用特定的服务器进行签名
|
||||||
|
|
||||||
PostFormat string // 上报格式 string or array
|
PostFormat string // 上报格式 string or array
|
||||||
Proxy string // 存储 proxy_rewrite,用于设置代理
|
Proxy string // 存储 proxy_rewrite,用于设置代理
|
||||||
@ -59,6 +62,8 @@ func Parse() {
|
|||||||
flag.StringVar(&LittleWD, "w", "", "cover the working directory")
|
flag.StringVar(&LittleWD, "w", "", "cover the working directory")
|
||||||
d := flag.Bool("D", false, "debug mode")
|
d := flag.Bool("D", false, "debug mode")
|
||||||
flag.BoolVar(&FastStart, "faststart", false, "skip waiting 5 seconds")
|
flag.BoolVar(&FastStart, "faststart", false, "skip waiting 5 seconds")
|
||||||
|
flag.BoolVar(&UpdateProtocol, "update-protocol", false, "update protocol")
|
||||||
|
flag.StringVar(&SignServerOverwrite, "sign-server", "", "use special server to sign tlv")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if *d {
|
if *d {
|
||||||
@ -79,6 +84,7 @@ func Init() {
|
|||||||
ExtraReplyData = conf.Message.ExtraReplyData
|
ExtraReplyData = conf.Message.ExtraReplyData
|
||||||
ForceFragmented = conf.Message.ForceFragment
|
ForceFragmented = conf.Message.ForceFragment
|
||||||
SkipMimeScan = conf.Message.SkipMimeScan
|
SkipMimeScan = conf.Message.SkipMimeScan
|
||||||
|
ConvertWebpImage = conf.Message.ConvertWebpImage
|
||||||
ReportSelfMessage = conf.Message.ReportSelfMessage
|
ReportSelfMessage = conf.Message.ReportSelfMessage
|
||||||
UseSSOAddress = conf.Account.UseSSOAddress
|
UseSSOAddress = conf.Account.UseSSOAddress
|
||||||
AllowTempSession = conf.Account.AllowTempSession
|
AllowTempSession = conf.Account.AllowTempSession
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
@ -32,6 +33,7 @@ var client = &http.Client{
|
|||||||
MaxIdleConns: 0,
|
MaxIdleConns: 0,
|
||||||
MaxIdleConnsPerHost: 999,
|
MaxIdleConnsPerHost: 999,
|
||||||
},
|
},
|
||||||
|
Timeout: time.Second * 15,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrOverSize 响应主体过大时返回此错误
|
// ErrOverSize 响应主体过大时返回此错误
|
||||||
@ -42,13 +44,18 @@ const UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
|
|||||||
|
|
||||||
// Request is a file download request
|
// Request is a file download request
|
||||||
type Request struct {
|
type Request struct {
|
||||||
|
Method string
|
||||||
URL string
|
URL string
|
||||||
Header map[string]string
|
Header map[string]string
|
||||||
Limit int64
|
Limit int64
|
||||||
|
Body io.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r Request) do() (*http.Response, error) {
|
func (r Request) do() (*http.Response, error) {
|
||||||
req, err := http.NewRequest(http.MethodGet, r.URL, nil)
|
if r.Method == "" {
|
||||||
|
r.Method = http.MethodGet
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest(r.Method, r.URL, r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -79,7 +86,7 @@ func (r Request) body() (io.ReadCloser, error) {
|
|||||||
return resp.Body, err
|
return resp.Body, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bytes 对给定URL发送Get请求,返回响应主体
|
// Bytes 对给定URL发送请求,返回响应主体
|
||||||
func (r Request) Bytes() ([]byte, error) {
|
func (r Request) Bytes() ([]byte, error) {
|
||||||
rd, err := r.body()
|
rd, err := r.body()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -89,7 +96,7 @@ func (r Request) Bytes() ([]byte, error) {
|
|||||||
return io.ReadAll(rd)
|
return io.ReadAll(rd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSON 发送GET请求, 并转换响应为JSON
|
// JSON 发送请求, 并转换响应为JSON
|
||||||
func (r Request) JSON() (gjson.Result, error) {
|
func (r Request) JSON() (gjson.Result, error) {
|
||||||
rd, err := r.body()
|
rd, err := r.body()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -111,6 +118,7 @@ func writeToFile(reader io.ReadCloser, path string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer func() { _ = file.Close() }()
|
||||||
_, err = file.ReadFrom(reader)
|
_, err = file.ReadFrom(reader)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
3
internal/encryption/config.go
Normal file
3
internal/encryption/config.go
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package encryption
|
||||||
|
|
||||||
|
var T544Signer = map[string]func(int64, []byte) []byte{}
|
10
internal/encryption/t544/cpuid_amd64.go
Normal file
10
internal/encryption/t544/cpuid_amd64.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
//go:build amd64
|
||||||
|
|
||||||
|
package t544
|
||||||
|
|
||||||
|
func cpuid(op uint32) (eax, ebx, ecx, edx uint32)
|
||||||
|
|
||||||
|
var canusesse2 = func() bool {
|
||||||
|
_, _, _, d := cpuid(1)
|
||||||
|
return d&(1<<26) > 0
|
||||||
|
}()
|
15
internal/encryption/t544/cpuid_amd64.s
Normal file
15
internal/encryption/t544/cpuid_amd64.s
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
//go:build amd64
|
||||||
|
// +build amd64
|
||||||
|
|
||||||
|
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
|
||||||
|
|
||||||
|
// func cpuid(op uint32) (eax, ebx, ecx, edx uint32)
|
||||||
|
TEXT ·cpuid(SB), 7, $0
|
||||||
|
XORQ CX, CX
|
||||||
|
MOVL op+0(FP), AX
|
||||||
|
CPUID
|
||||||
|
MOVL AX, eax+8(FP)
|
||||||
|
MOVL BX, ebx+12(FP)
|
||||||
|
MOVL CX, ecx+16(FP)
|
||||||
|
MOVL DX, edx+20(FP)
|
||||||
|
RET
|
30
internal/encryption/t544/data.go
Normal file
30
internal/encryption/t544/data.go
Normal file
File diff suppressed because one or more lines are too long
53
internal/encryption/t544/encryption.go
Normal file
53
internal/encryption/t544/encryption.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
//go:build amd64
|
||||||
|
|
||||||
|
package t544
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"hash/crc32"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
var crc32Table = func() (tab crc32.Table) {
|
||||||
|
f, err := cryptoZip.Open("crc32.bin")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
data, err := io.ReadAll(f)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for i := range tab {
|
||||||
|
tab[i] = binary.LittleEndian.Uint32(data[i*4 : (i+1)*4])
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}()
|
||||||
|
|
||||||
|
func tencentCrc32(tab *crc32.Table, b []byte) uint32
|
||||||
|
|
||||||
|
func sub_a([]byte, []uint32)
|
||||||
|
|
||||||
|
func sub_b([]byte, []uint32)
|
||||||
|
|
||||||
|
func sub_c(*[16][16]byte, []byte)
|
||||||
|
|
||||||
|
func sub_d(*[16]byte, []byte)
|
||||||
|
|
||||||
|
func sub_e(*[256][6]byte, []byte)
|
||||||
|
|
||||||
|
func sub_f(*[16]byte, *[15]uint32, *[16][16]byte) (w [44]uint32)
|
||||||
|
|
||||||
|
func sub_aa(int, *[16][2][16][16]byte, *[16]byte, []byte) byte
|
||||||
|
|
||||||
|
// transformInner see com/tencent/mobileqq/dt/model/FEBound
|
||||||
|
func transformInner(*[0x15]byte, *[32][16]byte)
|
||||||
|
|
||||||
|
func initState(*state, []byte, []byte, uint64)
|
||||||
|
|
||||||
|
func (c *state) init(key []byte, data []byte, counter uint64, nr uint8) {
|
||||||
|
c.nr = nr
|
||||||
|
c.p = 0
|
||||||
|
initState(c, key, data, counter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sub_ad([]uint32)
|
637
internal/encryption/t544/encryption_amd64.s
Normal file
637
internal/encryption/t544/encryption_amd64.s
Normal file
@ -0,0 +1,637 @@
|
|||||||
|
//go:build amd64
|
||||||
|
// +build amd64
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
DATA LC0<>+0(SB)/4, $1634760805
|
||||||
|
DATA LC0<>+4(SB)/4, $857760878
|
||||||
|
DATA LC0<>+8(SB)/4, $2036477234
|
||||||
|
DATA LC0<>+12(SB)/4, $1797285236
|
||||||
|
GLOBL LC0<>(SB), NOPTR, $16
|
||||||
|
|
||||||
|
TEXT ·sub_a(SB), NOSPLIT, $0-48
|
||||||
|
MOVQ ·a+0(FP), DI
|
||||||
|
MOVQ ·b+24(FP), CX
|
||||||
|
MOVQ CX, DX
|
||||||
|
MOVBLZX 3(CX), CX
|
||||||
|
XORB CX, (DI)
|
||||||
|
MOVBLZX 2(DX), CX
|
||||||
|
XORB CX, 1(DI)
|
||||||
|
MOVBLZX 1(DX), CX
|
||||||
|
XORB CX, 2(DI)
|
||||||
|
MOVBLZX (DX), CX
|
||||||
|
XORB CX, 3(DI)
|
||||||
|
MOVBLZX 7(DX), CX
|
||||||
|
XORB CX, 4(DI)
|
||||||
|
MOVBLZX 6(DX), CX
|
||||||
|
XORB CX, 5(DI)
|
||||||
|
MOVBLZX 5(DX), CX
|
||||||
|
XORB CX, 6(DI)
|
||||||
|
MOVBLZX 4(DX), CX
|
||||||
|
XORB CX, 7(DI)
|
||||||
|
MOVBLZX 11(DX),CX
|
||||||
|
XORB CX, 8(DI)
|
||||||
|
MOVBLZX 10(DX),CX
|
||||||
|
XORB CX, 9(DI)
|
||||||
|
MOVBLZX 9(DX), CX
|
||||||
|
XORB CX,10(DI)
|
||||||
|
MOVBLZX 8(DX), CX
|
||||||
|
XORB CX,11(DI)
|
||||||
|
MOVBLZX 15(DX),CX
|
||||||
|
XORB CX,12(DI)
|
||||||
|
MOVBLZX 14(DX),CX
|
||||||
|
XORB CX,13(DI)
|
||||||
|
MOVBLZX 13(DX),CX
|
||||||
|
XORB CX,14(DI)
|
||||||
|
MOVBLZX 12(DX),DX
|
||||||
|
XORB DL,15(DI)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·sub_b(SB), NOSPLIT, $0-48
|
||||||
|
MOVQ ·a+0(FP), DI
|
||||||
|
MOVQ ·b+24(FP), CX
|
||||||
|
MOVQ CX, DX
|
||||||
|
MOVBLZX 3(CX), CX
|
||||||
|
XORB CX, (DI)
|
||||||
|
MOVBLZX 6(DX), CX
|
||||||
|
XORB CX, 1(DI)
|
||||||
|
MOVBLZX 9(DX), CX
|
||||||
|
XORB CX, 2(DI)
|
||||||
|
MOVBLZX 12(DX),CX
|
||||||
|
XORB CX, 3(DI)
|
||||||
|
MOVBLZX 7(DX), CX
|
||||||
|
XORB CX, 4(DI)
|
||||||
|
MOVBLZX 10(DX),CX
|
||||||
|
XORB CX, 5(DI)
|
||||||
|
MOVBLZX 13(DX),CX
|
||||||
|
XORB CX, 6(DI)
|
||||||
|
MOVBLZX (DX), CX
|
||||||
|
XORB CX,7(DI)
|
||||||
|
MOVBLZX 11(DX),CX
|
||||||
|
XORB CX,8(DI)
|
||||||
|
MOVBLZX 14(DX),CX
|
||||||
|
XORB CX,9(DI)
|
||||||
|
MOVBLZX 1(DX), CX
|
||||||
|
XORB CX,10(DI)
|
||||||
|
MOVBLZX 4(DX), CX
|
||||||
|
XORB CX,11(DI)
|
||||||
|
MOVBLZX 15(DX),CX
|
||||||
|
XORB CX,12(DI)
|
||||||
|
MOVBLZX 2(DX), CX
|
||||||
|
XORB CX,13(DI)
|
||||||
|
MOVBLZX 5(DX), CX
|
||||||
|
XORB CX,14(DI)
|
||||||
|
MOVBLZX 8(DX), DX
|
||||||
|
XORB DL,15(DI)
|
||||||
|
RET
|
||||||
|
|
||||||
|
|
||||||
|
TEXT ·sub_c(SB), NOSPLIT, $0-32
|
||||||
|
MOVQ ·a+0(FP), DI
|
||||||
|
MOVQ ·b+8(FP), SI
|
||||||
|
MOVQ SI, AX
|
||||||
|
MOVBLZX (SI), SI
|
||||||
|
MOVL SI, CX
|
||||||
|
ANDL $15, SI
|
||||||
|
SHRL $4, CX
|
||||||
|
SHLL $4, CX
|
||||||
|
ADDL SI, CX
|
||||||
|
MOVBLZX 1(AX), SI
|
||||||
|
MOVLQSX CX, CX
|
||||||
|
MOVBLZX (DI)(CX*1), CX
|
||||||
|
MOVB CX, (AX)
|
||||||
|
MOVL SI, CX
|
||||||
|
ANDL $15, SI
|
||||||
|
SHRL $4, CX
|
||||||
|
SHLL $4, CX
|
||||||
|
ADDL SI, CX
|
||||||
|
MOVBLZX 2(AX), SI
|
||||||
|
MOVLQSX CX, CX
|
||||||
|
MOVBLZX (DI)(CX*1), CX
|
||||||
|
MOVB CX, 1(AX)
|
||||||
|
MOVL SI, CX
|
||||||
|
ANDL $15, SI
|
||||||
|
SHRL $4, CX
|
||||||
|
SHLL $4, CX
|
||||||
|
ADDL SI, CX
|
||||||
|
MOVBLZX 3(AX), SI
|
||||||
|
MOVLQSX CX, CX
|
||||||
|
MOVBLZX (DI)(CX*1), CX
|
||||||
|
MOVB CX, 2(AX)
|
||||||
|
MOVL SI, CX
|
||||||
|
ANDL $15, SI
|
||||||
|
SHRL $4, CX
|
||||||
|
SHLL $4, CX
|
||||||
|
ADDL SI, CX
|
||||||
|
MOVBLZX 4(AX), SI
|
||||||
|
MOVLQSX CX, CX
|
||||||
|
MOVBLZX (DI)(CX*1), CX
|
||||||
|
MOVB CX, 3(AX)
|
||||||
|
MOVL SI, CX
|
||||||
|
ANDL $15, SI
|
||||||
|
SHRL $4, CX
|
||||||
|
SHLL $4, CX
|
||||||
|
ADDL SI, CX
|
||||||
|
MOVBLZX 5(AX), SI
|
||||||
|
MOVLQSX CX, CX
|
||||||
|
MOVBLZX (DI)(CX*1), CX
|
||||||
|
MOVB CX, 4(AX)
|
||||||
|
MOVL SI, CX
|
||||||
|
ANDL $15, SI
|
||||||
|
SHRL $4, CX
|
||||||
|
SHLL $4, CX
|
||||||
|
ADDL SI, CX
|
||||||
|
MOVBLZX 6(AX), SI
|
||||||
|
MOVLQSX CX, CX
|
||||||
|
MOVBLZX (DI)(CX*1), CX
|
||||||
|
MOVB CX, 5(AX)
|
||||||
|
MOVL SI, CX
|
||||||
|
ANDL $15, SI
|
||||||
|
SHRL $4, CX
|
||||||
|
SHLL $4, CX
|
||||||
|
ADDL SI, CX
|
||||||
|
MOVBLZX 7(AX), SI
|
||||||
|
MOVLQSX CX, CX
|
||||||
|
MOVBLZX (DI)(CX*1), CX
|
||||||
|
MOVB CX, 6(AX)
|
||||||
|
MOVL SI, CX
|
||||||
|
ANDL $15, SI
|
||||||
|
SHRL $4, CX
|
||||||
|
SHLL $4, CX
|
||||||
|
ADDL SI, CX
|
||||||
|
MOVBLZX 8(AX), SI
|
||||||
|
MOVLQSX CX, CX
|
||||||
|
MOVBLZX (DI)(CX*1), CX
|
||||||
|
MOVB CX, 7(AX)
|
||||||
|
MOVL SI, CX
|
||||||
|
ANDL $15, SI
|
||||||
|
SHRL $4, CX
|
||||||
|
SHLL $4, CX
|
||||||
|
ADDL SI, CX
|
||||||
|
MOVBLZX 9(AX), SI
|
||||||
|
MOVLQSX CX, CX
|
||||||
|
MOVBLZX (DI)(CX*1), CX
|
||||||
|
MOVB CX, 8(AX)
|
||||||
|
MOVL SI, CX
|
||||||
|
ANDL $15, SI
|
||||||
|
SHRL $4, CX
|
||||||
|
SHLL $4, CX
|
||||||
|
ADDL SI, CX
|
||||||
|
MOVBLZX 10(AX), SI
|
||||||
|
MOVLQSX CX, CX
|
||||||
|
MOVBLZX (DI)(CX*1), CX
|
||||||
|
MOVB CX, 9(AX)
|
||||||
|
MOVL SI, CX
|
||||||
|
ANDL $15, SI
|
||||||
|
SHRL $4, CX
|
||||||
|
SHLL $4, CX
|
||||||
|
ADDL SI, CX
|
||||||
|
MOVBLZX 11(AX), SI
|
||||||
|
MOVLQSX CX, CX
|
||||||
|
MOVBLZX (DI)(CX*1), CX
|
||||||
|
MOVB CX, 10(AX)
|
||||||
|
MOVL SI, CX
|
||||||
|
ANDL $15, SI
|
||||||
|
SHRL $4, CX
|
||||||
|
SHLL $4, CX
|
||||||
|
ADDL SI, CX
|
||||||
|
MOVBLZX 12(AX), SI
|
||||||
|
MOVLQSX CX, CX
|
||||||
|
MOVBLZX (DI)(CX*1), CX
|
||||||
|
MOVB CX, 11(AX)
|
||||||
|
MOVL SI, CX
|
||||||
|
ANDL $15, SI
|
||||||
|
SHRL $4, CX
|
||||||
|
SHLL $4, CX
|
||||||
|
ADDL SI, CX
|
||||||
|
MOVBLZX 13(AX), SI
|
||||||
|
MOVLQSX CX, CX
|
||||||
|
MOVBLZX (DI)(CX*1), CX
|
||||||
|
MOVB CX, 12(AX)
|
||||||
|
MOVL SI, CX
|
||||||
|
ANDL $15, SI
|
||||||
|
SHRL $4, CX
|
||||||
|
SHLL $4, CX
|
||||||
|
ADDL SI, CX
|
||||||
|
MOVBLZX 14(AX), SI
|
||||||
|
MOVLQSX CX, CX
|
||||||
|
MOVBLZX (DI)(CX*1), CX
|
||||||
|
MOVB CX, 13(AX)
|
||||||
|
MOVL SI, CX
|
||||||
|
ANDL $15, SI
|
||||||
|
SHRL $4, CX
|
||||||
|
SHLL $4, CX
|
||||||
|
ADDL SI, CX
|
||||||
|
MOVBLZX 15(AX), SI
|
||||||
|
MOVLQSX CX, CX
|
||||||
|
MOVBLZX (DI)(CX*1), CX
|
||||||
|
MOVB CX, 14(AX)
|
||||||
|
MOVL SI, CX
|
||||||
|
ANDL $15, SI
|
||||||
|
SHRL $4, CX
|
||||||
|
SHLL $4, CX
|
||||||
|
ADDL SI, CX
|
||||||
|
MOVLQSX CX, CX
|
||||||
|
MOVBLZX (DI)(CX*1), CX
|
||||||
|
MOVB CX, 15(AX)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·sub_d(SB), NOSPLIT, $16-32
|
||||||
|
MOVQ ·t+0(FP), BX
|
||||||
|
MOVQ ·s+8(FP), SI
|
||||||
|
MOVOU (SI), X0
|
||||||
|
MOVOU X0, in-16(SP)
|
||||||
|
MOVQ SI, DI
|
||||||
|
ADDQ $15, DI
|
||||||
|
MOVB $16, CX
|
||||||
|
STD
|
||||||
|
lop:
|
||||||
|
LEAQ -1(CX), AX
|
||||||
|
XLAT
|
||||||
|
MOVBLZX in-16(SP)(AX*1), AX
|
||||||
|
STOSB
|
||||||
|
LOOP lop
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·sub_e(SB), NOSPLIT, $0-32
|
||||||
|
MOVQ ·a+0(FP), DI
|
||||||
|
MOVQ ·n+8(FP), SI
|
||||||
|
MOVQ $4, AX
|
||||||
|
lop:
|
||||||
|
MOVBQZX -4(SI)(AX*4), DX
|
||||||
|
MOVBQZX -3(SI)(AX*4), CX
|
||||||
|
MOVBQZX -2(SI)(AX*4), R10
|
||||||
|
MOVBQZX -1(SI)(AX*4), R8
|
||||||
|
LEAQ (DX)(DX*2), R9
|
||||||
|
LEAQ (R9*2), R9
|
||||||
|
LEAQ (CX)(CX*2), R11
|
||||||
|
LEAQ (R11*2), R11
|
||||||
|
LEAQ (R10)(R10*2), BX
|
||||||
|
LEAQ (BX*2), BX
|
||||||
|
MOVB DX, R13
|
||||||
|
XORB CX, DX
|
||||||
|
XORB R10, CX
|
||||||
|
MOVB (DI)(R9*1), R12
|
||||||
|
XORB 1(DI)(R11*1), R12
|
||||||
|
XORB R8, R10
|
||||||
|
XORB R12, R10
|
||||||
|
MOVB R10, -4(SI)(AX*4)
|
||||||
|
MOVB (DI)(R11*1), R10
|
||||||
|
XORB 1(DI)(BX*1), R10
|
||||||
|
XORB R8, R13
|
||||||
|
XORB R10, R13
|
||||||
|
MOVB R13, -3(SI)(AX*4)
|
||||||
|
MOVB (DI)(BX*1), R10
|
||||||
|
LEAQ (R8)(R8*2), R8
|
||||||
|
LEAQ (R8*2), R8
|
||||||
|
XORB 1(DI)(R8*1), R10
|
||||||
|
XORB R10, DX
|
||||||
|
MOVB DX, -2(SI)(AX*4)
|
||||||
|
MOVB 1(DI)(R9*1), DX
|
||||||
|
XORB (DI)(R8*1), DX
|
||||||
|
XORB DX, CX
|
||||||
|
MOVB CX, -1(SI)(AX*4)
|
||||||
|
DECB AX
|
||||||
|
JNZ lop
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT sub_ab(SB), NOSPLIT, $0-24
|
||||||
|
MOVQ ·s+0(FP), DI
|
||||||
|
MOVQ ·w+8(FP), SI
|
||||||
|
MOVL SI, AX
|
||||||
|
MOVL SI, CX
|
||||||
|
MOVL SI, DX
|
||||||
|
SHRL $28, AX
|
||||||
|
SHRL $24, CX
|
||||||
|
ANDL $15, CX
|
||||||
|
SALL $4, AX
|
||||||
|
ADDL CX, AX
|
||||||
|
MOVBLZX SI, CX
|
||||||
|
MOVBLZX (DI)(AX*1), AX
|
||||||
|
MOVBLZX (DI)(CX*1), CX
|
||||||
|
SALL $24, AX
|
||||||
|
ORL CX, AX
|
||||||
|
MOVL SI, CX
|
||||||
|
SHRL $8, SI
|
||||||
|
SHRL $8, CX
|
||||||
|
ANDL $15, SI
|
||||||
|
ANDL $240, CX
|
||||||
|
ADDL SI, CX
|
||||||
|
MOVBLZX (DI)(CX*1), CX
|
||||||
|
SALL $8, CX
|
||||||
|
ORL CX, AX
|
||||||
|
MOVL DX, CX
|
||||||
|
SHRL $16, DX
|
||||||
|
SHRL $16, CX
|
||||||
|
ANDL $15, DX
|
||||||
|
ANDL $240, CX
|
||||||
|
ADDL CX, DX
|
||||||
|
MOVBLZX (DI)(DX*1), DX
|
||||||
|
SALL $16, DX
|
||||||
|
ORL DX, AX
|
||||||
|
MOVQ AX, ·retval+16(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·sub_f(SB), NOSPLIT, $24-68
|
||||||
|
MOVQ ·k+0(FP), DI
|
||||||
|
MOVQ ·r+8(FP), SI
|
||||||
|
MOVQ ·s+16(FP), DX
|
||||||
|
MOVQ $·w+24(FP), CX
|
||||||
|
MOVQ CX, R10
|
||||||
|
MOVQ SI, R9
|
||||||
|
MOVQ DX, R8
|
||||||
|
MOVL $4, BX
|
||||||
|
MOVL (DI), AX
|
||||||
|
BSWAPL AX
|
||||||
|
MOVL AX, (CX)
|
||||||
|
MOVL 4(DI), AX
|
||||||
|
BSWAPL AX
|
||||||
|
MOVL AX, 4(CX)
|
||||||
|
MOVL 8(DI), AX
|
||||||
|
BSWAPL AX
|
||||||
|
MOVL AX, 8(CX)
|
||||||
|
MOVL 12(DI), AX
|
||||||
|
BSWAPL AX
|
||||||
|
MOVL AX, 12(CX)
|
||||||
|
JMP inner
|
||||||
|
for:
|
||||||
|
XORL -16(R10)(BX*4), AX
|
||||||
|
MOVL AX, (R10)(BX*4)
|
||||||
|
ADDQ $1, BX
|
||||||
|
CMPQ BX, $44
|
||||||
|
JE end
|
||||||
|
inner:
|
||||||
|
MOVL -4(R10)(BX*4), AX
|
||||||
|
TESTB $3, BX
|
||||||
|
JNE for
|
||||||
|
ROLL $8, AX
|
||||||
|
MOVQ R8, 0(SP)
|
||||||
|
MOVL AX, 8(SP)
|
||||||
|
CALL sub_ab(SB)
|
||||||
|
MOVQ 16(SP), AX
|
||||||
|
LEAL -1(BX), DX
|
||||||
|
SARL $2, DX
|
||||||
|
MOVLQSX DX, DX
|
||||||
|
XORL (R9)(DX*4), AX
|
||||||
|
JMP for
|
||||||
|
end:
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·sub_aa(SB), NOSPLIT, $0-56
|
||||||
|
MOVQ ·i+0(FP), DI
|
||||||
|
MOVQ ·t+8(FP), SI
|
||||||
|
MOVQ ·b+16(FP), DX
|
||||||
|
MOVQ ·m+24(FP), CX
|
||||||
|
MOVL DI, AX
|
||||||
|
MOVLQSX DI, DI
|
||||||
|
MOVQ SI, R8
|
||||||
|
MOVQ DX, SI
|
||||||
|
MOVBLZX (CX)(DI*1), CX
|
||||||
|
ANDL $15, AX
|
||||||
|
MOVBLZX (SI)(AX*1), SI
|
||||||
|
MOVQ AX, DX
|
||||||
|
MOVL CX, AX
|
||||||
|
SALQ $9, DX
|
||||||
|
ANDL $15, CX
|
||||||
|
SHRB $4, AX
|
||||||
|
MOVL SI, DI
|
||||||
|
ADDQ R8, DX
|
||||||
|
SALQ $4, CX
|
||||||
|
ANDL $15, AX
|
||||||
|
SHRB $4, DI
|
||||||
|
ANDL $15, SI
|
||||||
|
SALQ $4, AX
|
||||||
|
ANDL $15, DI
|
||||||
|
ADDQ DX, AX
|
||||||
|
ADDQ CX, DX
|
||||||
|
MOVBLZX (AX)(DI*1), AX
|
||||||
|
SALL $4, AX
|
||||||
|
ORB 256(SI)(DX*1), AX
|
||||||
|
MOVQ AX, ·retval+48(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func transformInner(x *[0x15]byte, tab *[32][16]byte)
|
||||||
|
TEXT ·transformInner(SB), NOSPLIT, $0-16
|
||||||
|
MOVQ ·x+0(FP), DI
|
||||||
|
MOVQ ·tab+8(FP), SI
|
||||||
|
MOVQ DI, AX
|
||||||
|
MOVL $1, CX
|
||||||
|
MOVQ SI, DI
|
||||||
|
MOVQ AX, SI
|
||||||
|
lop:
|
||||||
|
MOVBLZX (SI), R8
|
||||||
|
LEAL -1(CX), AX
|
||||||
|
ADDQ $1, SI
|
||||||
|
ANDL $31, AX
|
||||||
|
MOVL R8, DX
|
||||||
|
SALL $4, AX
|
||||||
|
ANDL $15, R8
|
||||||
|
SHRB $4, DX
|
||||||
|
MOVBLZX DX, DX
|
||||||
|
ADDL DX, AX
|
||||||
|
CDQE
|
||||||
|
MOVBLSX (DI)(AX*1), AX
|
||||||
|
SALL $4, AX
|
||||||
|
MOVL AX, DX
|
||||||
|
MOVL CX, AX
|
||||||
|
ADDL $2, CX
|
||||||
|
ANDL $31, AX
|
||||||
|
SALL $4, AX
|
||||||
|
ADDL R8, AX
|
||||||
|
CDQE
|
||||||
|
ORB (DI)(AX*1), DX
|
||||||
|
MOVB DX, -1(SI)
|
||||||
|
CMPL CX, $43
|
||||||
|
JNE lop
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·initState(SB), NOSPLIT, $0-64
|
||||||
|
MOVQ ·c+0(FP), DI
|
||||||
|
MOVQ ·key+8(FP), SI
|
||||||
|
MOVQ ·data+32(FP), R8
|
||||||
|
MOVQ ·counter+56(FP), AX
|
||||||
|
MOVOA LC0<>(SB), X0
|
||||||
|
MOVUPS X0, (DI)
|
||||||
|
MOVOU (SI), X1
|
||||||
|
MOVOU (DI), X3
|
||||||
|
MOVUPS X1, 16(DI)
|
||||||
|
MOVOU 16(SI), X2
|
||||||
|
MOVQ AX, 48(DI)
|
||||||
|
MOVUPS X2, 32(DI)
|
||||||
|
MOVQ (R8), AX
|
||||||
|
MOVUPS X3, 64(DI)
|
||||||
|
MOVQ AX, 56(DI)
|
||||||
|
MOVQ 48(DI), AX
|
||||||
|
MOVUPS X1, 80(DI)
|
||||||
|
MOVUPS X2, 96(DI)
|
||||||
|
MOVUPS X6,112(DI)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·sub_ad(SB), NOSPLIT, $8-24
|
||||||
|
MOVQ ·a+0(FP), DI
|
||||||
|
MOVQ DI, AX
|
||||||
|
MOVL 40(DI), R10
|
||||||
|
MOVL 12(DI), R12
|
||||||
|
MOVL 44(DI), BP
|
||||||
|
MOVL 16(DI), DX
|
||||||
|
MOVL (DI), R15
|
||||||
|
MOVL 48(DI), R9
|
||||||
|
MOVL 20(DI), SI
|
||||||
|
MOVL 32(DI), R11
|
||||||
|
ADDL DX, R15
|
||||||
|
MOVL 4(DI), R14
|
||||||
|
MOVL 52(DI), R8
|
||||||
|
XORL R15, R9
|
||||||
|
MOVL 24(DI), CX
|
||||||
|
MOVL 8(DI), R13
|
||||||
|
ROLL $16, R9
|
||||||
|
ADDL SI, R14
|
||||||
|
MOVL 36(DI), BX
|
||||||
|
MOVL 56(DI), DI
|
||||||
|
ADDL R9, R11
|
||||||
|
XORL R14, R8
|
||||||
|
ADDL CX, R13
|
||||||
|
XORL R11, DX
|
||||||
|
ROLL $16, R8
|
||||||
|
XORL R13, DI
|
||||||
|
ROLL $12, DX
|
||||||
|
ADDL R8, BX
|
||||||
|
ROLL $16, DI
|
||||||
|
ADDL DX, R15
|
||||||
|
XORL BX, SI
|
||||||
|
ADDL DI, R10
|
||||||
|
XORL R15, R9
|
||||||
|
ROLL $12, SI
|
||||||
|
XORL R10, CX
|
||||||
|
ROLL $8, R9
|
||||||
|
ADDL SI, R14
|
||||||
|
ROLL $12, CX
|
||||||
|
ADDL R9, R11
|
||||||
|
XORL R14, R8
|
||||||
|
ADDL CX, R13
|
||||||
|
XORL R11, DX
|
||||||
|
ROLL $8, R8
|
||||||
|
XORL R13, DI
|
||||||
|
ROLL $7, DX
|
||||||
|
LEAL (BX)(R8*1), BX
|
||||||
|
ROLL $8, DI
|
||||||
|
MOVL DX, tmp0-8(SP)
|
||||||
|
MOVL 28(AX), DX
|
||||||
|
XORL BX, SI
|
||||||
|
MOVL BX, tmp1-4(SP)
|
||||||
|
MOVL R10, BX
|
||||||
|
MOVL 60(AX), R10
|
||||||
|
ROLL $7, SI
|
||||||
|
ADDL DI, BX
|
||||||
|
ADDL DX, R12
|
||||||
|
ADDL SI, R15
|
||||||
|
XORL R12, R10
|
||||||
|
XORL BX, CX
|
||||||
|
ROLL $16, R10
|
||||||
|
ROLL $7, CX
|
||||||
|
ADDL R10, BP
|
||||||
|
ADDL CX, R14
|
||||||
|
XORL BP, DX
|
||||||
|
XORL R14, R9
|
||||||
|
ROLL $12, DX
|
||||||
|
ROLL $16, R9
|
||||||
|
ADDL DX, R12
|
||||||
|
XORL R12, R10
|
||||||
|
ROLL $8, R10
|
||||||
|
ADDL R10, BP
|
||||||
|
XORL R15, R10
|
||||||
|
ROLL $16, R10
|
||||||
|
XORL BP, DX
|
||||||
|
ADDL R9, BP
|
||||||
|
ADDL R10, BX
|
||||||
|
ROLL $7, DX
|
||||||
|
XORL BP, CX
|
||||||
|
XORL BX, SI
|
||||||
|
ROLL $12, SI
|
||||||
|
ADDL SI, R15
|
||||||
|
XORL R15, R10
|
||||||
|
MOVL R15, (AX)
|
||||||
|
ROLL $8, R10
|
||||||
|
ADDL R10, BX
|
||||||
|
MOVL R10, 60(AX)
|
||||||
|
XORL BX, SI
|
||||||
|
MOVD BX, X1
|
||||||
|
ROLL $7, SI
|
||||||
|
ROLL $12, CX
|
||||||
|
ADDL DX, R13
|
||||||
|
XORL R13, R8
|
||||||
|
ADDL CX, R14
|
||||||
|
MOVL SI, 20(AX)
|
||||||
|
ROLL $16, R8
|
||||||
|
XORL R14, R9
|
||||||
|
MOVL R14, 4(AX)
|
||||||
|
ADDL R8, R11
|
||||||
|
ROLL $8, R9
|
||||||
|
XORL R11, DX
|
||||||
|
ADDL R9, BP
|
||||||
|
MOVL R9, 48(AX)
|
||||||
|
ROLL $12, DX
|
||||||
|
XORL BP, CX
|
||||||
|
MOVD BP, X2
|
||||||
|
ADDL DX, R13
|
||||||
|
ROLL $7, CX
|
||||||
|
PUNPCKLLQ X2, X1
|
||||||
|
XORL R13, R8
|
||||||
|
MOVL CX, 24(AX)
|
||||||
|
ROLL $8, R8
|
||||||
|
MOVL R13, 8(AX)
|
||||||
|
ADDL R8, R11
|
||||||
|
XORL R11, DX
|
||||||
|
MOVD R11, X0
|
||||||
|
ROLL $7, DX
|
||||||
|
MOVL DX, 28(AX)
|
||||||
|
MOVL R8, 52(AX)
|
||||||
|
MOVL tmp0-8(SP), SI
|
||||||
|
MOVL tmp1-4(SP), CX
|
||||||
|
ADDL SI, R12
|
||||||
|
XORL R12, DI
|
||||||
|
ROLL $16, DI
|
||||||
|
ADDL DI, CX
|
||||||
|
XORL CX, SI
|
||||||
|
MOVL SI, DX
|
||||||
|
ROLL $12, DX
|
||||||
|
ADDL DX, R12
|
||||||
|
XORL R12, DI
|
||||||
|
MOVL R12, 12(AX)
|
||||||
|
ROLL $8, DI
|
||||||
|
ADDL DI, CX
|
||||||
|
MOVL DI, 56(AX)
|
||||||
|
MOVD CX, X3
|
||||||
|
XORL CX, DX
|
||||||
|
PUNPCKLLQ X3, X0
|
||||||
|
ROLL $7, DX
|
||||||
|
PUNPCKLQDQ X1, X0
|
||||||
|
MOVL DX, 16(AX)
|
||||||
|
MOVUPS X0, 32(AX)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func tencentCrc32(tab *crc32.Table, b []byte) uint32
|
||||||
|
TEXT ·tencentCrc32(SB), NOSPLIT, $0-40
|
||||||
|
MOVQ ·tab+0(FP), DI
|
||||||
|
MOVQ ·bptr+8(FP), SI
|
||||||
|
MOVQ ·bngas+16(FP), DX
|
||||||
|
TESTQ DX, DX
|
||||||
|
JE quickend
|
||||||
|
ADDQ SI, DX
|
||||||
|
MOVL $-1, AX
|
||||||
|
lop:
|
||||||
|
MOVBLZX (SI), CX
|
||||||
|
ADDQ $1, SI
|
||||||
|
XORL AX, CX
|
||||||
|
SHRL $8, AX
|
||||||
|
MOVBLZX CX, CX
|
||||||
|
XORL (DI)(CX*4), AX
|
||||||
|
CMPQ SI, DX
|
||||||
|
JNE lop
|
||||||
|
NOTL AX
|
||||||
|
MOVQ AX, ·bngas+32(FP)
|
||||||
|
RET
|
||||||
|
quickend:
|
||||||
|
XORL AX, AX
|
||||||
|
RET
|
||||||
|
|
117
internal/encryption/t544/encryption_generic.go
Normal file
117
internal/encryption/t544/encryption_generic.go
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
//go:build amd64
|
||||||
|
|
||||||
|
package t544
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type encryptionData struct {
|
||||||
|
tableA [16][2][16][16]byte
|
||||||
|
tableB [16][16]byte
|
||||||
|
tableC [256][6]byte
|
||||||
|
tableD [16]byte
|
||||||
|
tableE [16]byte
|
||||||
|
tableF [15]uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type state struct {
|
||||||
|
state [16]uint32 // 16
|
||||||
|
orgstate [16]uint32 // 16
|
||||||
|
nr uint8
|
||||||
|
p uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
var crypto = encryptionData{
|
||||||
|
tableA: readData[[16][2][16][16]byte]("table_a.bin"),
|
||||||
|
tableB: readData[[16][16]byte]("table_b.bin"),
|
||||||
|
tableC: readData[[256][6]byte]("table_c.bin"),
|
||||||
|
tableD: readData[[16]byte]("table_d.bin"),
|
||||||
|
tableE: readData[[16]byte]("table_e.bin"),
|
||||||
|
tableF: func() (tab [15]uint32) {
|
||||||
|
f, err := cryptoZip.Open("table_f.bin")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
data, err := io.ReadAll(f)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for i := range tab {
|
||||||
|
tab[i] = binary.LittleEndian.Uint32(data[i*4 : (i+1)*4])
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}(),
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *encryptionData) tencentEncryptB(p1 []byte, p2 []uint32) {
|
||||||
|
const c = 10
|
||||||
|
for r := 0; r < 9; r++ {
|
||||||
|
sub_d(&e.tableD, p1)
|
||||||
|
sub_b(p1, p2[r*4:(r+1)*4])
|
||||||
|
sub_c(&e.tableB, p1)
|
||||||
|
sub_e(&e.tableC, p1)
|
||||||
|
}
|
||||||
|
sub_d(&e.tableD, p1)
|
||||||
|
sub_b(p1, p2[(c-1)*4:c*4])
|
||||||
|
sub_c(&e.tableB, p1)
|
||||||
|
sub_a(p1, p2[c*4:(c+1)*4])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *encryptionData) tencentEncryptionB(c []byte, m []byte) (out [0x15]byte) {
|
||||||
|
var buf [16]byte
|
||||||
|
w := sub_f(&e.tableE, &e.tableF, &e.tableB)
|
||||||
|
|
||||||
|
for i := range out {
|
||||||
|
if (i & 0xf) == 0 {
|
||||||
|
copy(buf[:], c)
|
||||||
|
e.tencentEncryptB(buf[:], w[:])
|
||||||
|
for j := 15; j >= 0; j-- {
|
||||||
|
c[j]++
|
||||||
|
if c[j] != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out[i] = sub_aa(i, &e.tableA, &buf, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func tencentEncryptionA(input, key, data []byte) {
|
||||||
|
var s state
|
||||||
|
s.init(key, data, 0, 20)
|
||||||
|
s.encrypt(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *state) encrypt(data []byte) {
|
||||||
|
bp := 0
|
||||||
|
dataLen := uint32(len(data))
|
||||||
|
for dataLen > 0 {
|
||||||
|
if c.p == 0 {
|
||||||
|
for i := uint8(0); i < c.nr; i += 2 {
|
||||||
|
sub_ad(c.state[:])
|
||||||
|
}
|
||||||
|
for i := 0; i < 16; i++ {
|
||||||
|
c.state[i] += c.orgstate[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var sb [16 * 4]byte
|
||||||
|
for i, v := range c.state {
|
||||||
|
binary.LittleEndian.PutUint32(sb[i*4:(i+1)*4], v)
|
||||||
|
}
|
||||||
|
for c.p != 64 && dataLen != 0 {
|
||||||
|
data[bp] ^= sb[c.p]
|
||||||
|
c.p++
|
||||||
|
bp++
|
||||||
|
dataLen--
|
||||||
|
}
|
||||||
|
if c.p >= 64 {
|
||||||
|
c.p = 0
|
||||||
|
c.orgstate[12]++
|
||||||
|
c.state = c.orgstate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
93
internal/encryption/t544/t544.go
Normal file
93
internal/encryption/t544/t544.go
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
//go:build amd64
|
||||||
|
|
||||||
|
package t544
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/rc4"
|
||||||
|
"encoding/binary"
|
||||||
|
"math/rand"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/Mrs4s/go-cqhttp/internal/encryption"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
keyTable = "$%&()+,-456789:?ABCDEEFGHIJabcdefghijkopqrstuvwxyz"
|
||||||
|
table2 = "!#$%&)+.0123456789:=>?@ABCDEFGKMNabcdefghijkopqrst"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
magic = uint64(0x6EEDCF0DC4675540)
|
||||||
|
key1 = [8]byte{'a', '$', '(', 'e', 'T', '7', '*', '@'}
|
||||||
|
key2 = [8]byte{'&', 'O', '9', '!', '>', '6', 'X', ')'}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if canusesse2 {
|
||||||
|
encryption.T544Signer["8.9.35.10440"] = sign
|
||||||
|
encryption.T544Signer["8.9.38.10545"] = sign
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sign t544 algorithm
|
||||||
|
// special thanks to the anonymous contributor who provided the algorithm
|
||||||
|
func sign(curr int64, input []byte) []byte {
|
||||||
|
curr %= 1000000
|
||||||
|
input = append(input, []byte{byte(curr >> 24), byte(curr >> 16), byte(curr >> 8), byte(curr)}...)
|
||||||
|
var kt [4 + 32 + 4]byte
|
||||||
|
r := rand.New(rand.NewSource(curr))
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
kt[i] = keyTable[r.Int()%0x32] + 50
|
||||||
|
}
|
||||||
|
kt[2] = kt[1] + 20
|
||||||
|
kt[3] = kt[2] + 20
|
||||||
|
key3 := kt[4 : 4+10]
|
||||||
|
k3calc := key3[2:10]
|
||||||
|
copy(k3calc, key1[:4])
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
k3calc[4+i] = key2[i] ^ kt[i]
|
||||||
|
}
|
||||||
|
key3[0], key3[1] = k3calc[6], k3calc[7]
|
||||||
|
key3 = key3[:8]
|
||||||
|
k3calc[6], k3calc[7] = 0, 0
|
||||||
|
rc4Cipher, _ := rc4.NewCipher(key3)
|
||||||
|
rc4Cipher.XORKeyStream(key3, key3)
|
||||||
|
var crcData [0x15]byte
|
||||||
|
copy(crcData[4:4+8], (*[8]byte)(unsafe.Pointer(&magic))[:])
|
||||||
|
tencentEncryptionA(input, kt[4:4+32], crcData[4:4+8])
|
||||||
|
result := md5.Sum(input)
|
||||||
|
crcData[2] = 1
|
||||||
|
crcData[4] = 1
|
||||||
|
copy(crcData[5:9], kt[:4])
|
||||||
|
binary.BigEndian.PutUint32(crcData[9:13], uint32(curr))
|
||||||
|
copy(crcData[13:], result[:8])
|
||||||
|
calcCrc := tencentCrc32(&crc32Table, crcData[2:])
|
||||||
|
copy(kt[4+32:4+32+4], (*[4]byte)(unsafe.Pointer(&calcCrc))[:])
|
||||||
|
crcData[0] = kt[4+32]
|
||||||
|
crcData[1] = kt[4+32+3]
|
||||||
|
nonce := uint32(r.Int() ^ r.Int() ^ r.Int())
|
||||||
|
on := kt[:16]
|
||||||
|
binary.BigEndian.PutUint32(on[:4], nonce)
|
||||||
|
copy(on[4:8], on[:4])
|
||||||
|
copy(on[8:16], on[:8])
|
||||||
|
ts.transformEncode(&crcData)
|
||||||
|
encryptedData := crypto.tencentEncryptionB(on, crcData[:])
|
||||||
|
ts.transformDecode(&encryptedData)
|
||||||
|
output := kt[:39]
|
||||||
|
output[0] = 0x0C
|
||||||
|
output[1] = 0x05
|
||||||
|
binary.BigEndian.PutUint32(output[2:6], nonce)
|
||||||
|
copy(output[6:27], encryptedData[:])
|
||||||
|
binary.LittleEndian.PutUint32(output[27:31], 0)
|
||||||
|
output[31] = table2[r.Int()%0x32]
|
||||||
|
output[32] = table2[r.Int()%0x32]
|
||||||
|
addition := r.Int() % 9
|
||||||
|
for addition&1 == 0 {
|
||||||
|
addition = r.Int() % 9
|
||||||
|
}
|
||||||
|
output[33] = output[31] + byte(addition)
|
||||||
|
output[34] = output[32] + byte(9-addition) + 1
|
||||||
|
binary.LittleEndian.PutUint32(output[35:39], 0)
|
||||||
|
return output
|
||||||
|
}
|
7
internal/encryption/t544/t544_stub.go
Normal file
7
internal/encryption/t544/t544_stub.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
//go:build !amd64
|
||||||
|
|
||||||
|
package t544
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
}
|
21
internal/encryption/t544/transform.go
Normal file
21
internal/encryption/t544/transform.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
//go:build amd64
|
||||||
|
|
||||||
|
package t544
|
||||||
|
|
||||||
|
type transformer struct {
|
||||||
|
encode [32][16]byte
|
||||||
|
decode [32][16]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ts *transformer) transformEncode(bArr *[0x15]byte) {
|
||||||
|
transformInner(bArr, &ts.encode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ts *transformer) transformDecode(bArr *[0x15]byte) {
|
||||||
|
transformInner(bArr, &ts.decode)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ts = transformer{
|
||||||
|
encode: readData[[32][16]byte]("encode.bin"),
|
||||||
|
decode: readData[[32][16]byte]("decode.bin"),
|
||||||
|
}
|
246
internal/msg/element.go
Normal file
246
internal/msg/element.go
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
// Package msg 提供了go-cqhttp消息中间表示,CQ码处理等等
|
||||||
|
package msg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
)
|
||||||
|
|
||||||
|
// @@@ CQ码转义处理 @@@
|
||||||
|
|
||||||
|
// EscapeText 将字符串raw中部分字符转义
|
||||||
|
//
|
||||||
|
// - & -> &
|
||||||
|
// - [ -> [
|
||||||
|
// - ] -> ]
|
||||||
|
func EscapeText(s string) string {
|
||||||
|
count := strings.Count(s, "&")
|
||||||
|
count += strings.Count(s, "[")
|
||||||
|
count += strings.Count(s, "]")
|
||||||
|
if count == 0 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply replacements to buffer.
|
||||||
|
var b strings.Builder
|
||||||
|
b.Grow(len(s) + count*4)
|
||||||
|
start := 0
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
j := start
|
||||||
|
for index, r := range s[start:] {
|
||||||
|
if r == '&' || r == '[' || r == ']' {
|
||||||
|
j += index
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.WriteString(s[start:j])
|
||||||
|
switch s[j] {
|
||||||
|
case '&':
|
||||||
|
b.WriteString("&")
|
||||||
|
case '[':
|
||||||
|
b.WriteString("[")
|
||||||
|
case ']':
|
||||||
|
b.WriteString("]")
|
||||||
|
}
|
||||||
|
start = j + 1
|
||||||
|
}
|
||||||
|
b.WriteString(s[start:])
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// EscapeValue 将字符串value中部分字符转义
|
||||||
|
//
|
||||||
|
// - , -> ,
|
||||||
|
// - & -> &
|
||||||
|
// - [ -> [
|
||||||
|
// - ] -> ]
|
||||||
|
func EscapeValue(value string) string {
|
||||||
|
ret := EscapeText(value)
|
||||||
|
return strings.ReplaceAll(ret, ",", ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnescapeText 将字符串content中部分字符反转义
|
||||||
|
//
|
||||||
|
// - & -> &
|
||||||
|
// - [ -> [
|
||||||
|
// - ] -> ]
|
||||||
|
func UnescapeText(content string) string {
|
||||||
|
ret := content
|
||||||
|
ret = strings.ReplaceAll(ret, "[", "[")
|
||||||
|
ret = strings.ReplaceAll(ret, "]", "]")
|
||||||
|
ret = strings.ReplaceAll(ret, "&", "&")
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnescapeValue 将字符串content中部分字符反转义
|
||||||
|
//
|
||||||
|
// - , -> ,
|
||||||
|
// - & -> &
|
||||||
|
// - [ -> [
|
||||||
|
// - ] -> ]
|
||||||
|
func UnescapeValue(content string) string {
|
||||||
|
ret := strings.ReplaceAll(content, ",", ",")
|
||||||
|
return UnescapeText(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @@@ 消息中间表示 @@@
|
||||||
|
|
||||||
|
// Pair key value pair
|
||||||
|
type Pair struct {
|
||||||
|
K string
|
||||||
|
V string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Element single message
|
||||||
|
type Element struct {
|
||||||
|
Type string
|
||||||
|
Data []Pair
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get 获取指定值
|
||||||
|
func (e *Element) Get(k string) string {
|
||||||
|
for _, datum := range e.Data {
|
||||||
|
if datum.K == k {
|
||||||
|
return datum.V
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// CQCode convert element to cqcode
|
||||||
|
func (e *Element) CQCode() string {
|
||||||
|
buf := strings.Builder{}
|
||||||
|
e.WriteCQCodeTo(&buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteCQCodeTo write element's cqcode into sb
|
||||||
|
func (e *Element) WriteCQCodeTo(sb *strings.Builder) {
|
||||||
|
if e.Type == "text" {
|
||||||
|
sb.WriteString(EscapeText(e.Data[0].V)) // must be {"text": value}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sb.WriteString("[CQ:")
|
||||||
|
sb.WriteString(e.Type)
|
||||||
|
for _, data := range e.Data {
|
||||||
|
sb.WriteByte(',')
|
||||||
|
sb.WriteString(data.K)
|
||||||
|
sb.WriteByte('=')
|
||||||
|
sb.WriteString(EscapeValue(data.V))
|
||||||
|
}
|
||||||
|
sb.WriteByte(']')
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON see encoding/json.Marshaler
|
||||||
|
func (e *Element) MarshalJSON() ([]byte, error) {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
buf := (*bytes.Buffer)(w)
|
||||||
|
// fmt.Fprintf(buf, `{"type":"%s","data":{`, e.Type)
|
||||||
|
buf.WriteString(`{"type":"`)
|
||||||
|
buf.WriteString(e.Type)
|
||||||
|
buf.WriteString(`","data":{`)
|
||||||
|
for i, data := range e.Data {
|
||||||
|
if i != 0 {
|
||||||
|
buf.WriteByte(',')
|
||||||
|
}
|
||||||
|
// fmt.Fprintf(buf, `"%s":%q`, data.K, data.V)
|
||||||
|
buf.WriteByte('"')
|
||||||
|
buf.WriteString(data.K)
|
||||||
|
buf.WriteString(`":`)
|
||||||
|
buf.WriteString(QuoteJSON(data.V))
|
||||||
|
}
|
||||||
|
buf.WriteString(`}}`)
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const hex = "0123456789abcdef"
|
||||||
|
|
||||||
|
// QuoteJSON 按JSON转义为字符加上双引号
|
||||||
|
func QuoteJSON(s string) string {
|
||||||
|
i, j := 0, 0
|
||||||
|
var b strings.Builder
|
||||||
|
b.WriteByte('"')
|
||||||
|
for j < len(s) {
|
||||||
|
c := s[j]
|
||||||
|
|
||||||
|
if c >= 0x20 && c <= 0x7f && c != '\\' && c != '"' {
|
||||||
|
// fast path: most of the time, printable ascii characters are used
|
||||||
|
j++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch c {
|
||||||
|
case '\\', '"', '\n', '\r', '\t':
|
||||||
|
b.WriteString(s[i:j])
|
||||||
|
b.WriteByte('\\')
|
||||||
|
switch c {
|
||||||
|
case '\n':
|
||||||
|
c = 'n'
|
||||||
|
case '\r':
|
||||||
|
c = 'r'
|
||||||
|
case '\t':
|
||||||
|
c = 't'
|
||||||
|
}
|
||||||
|
b.WriteByte(c)
|
||||||
|
j++
|
||||||
|
i = j
|
||||||
|
continue
|
||||||
|
|
||||||
|
case '<', '>', '&':
|
||||||
|
b.WriteString(s[i:j])
|
||||||
|
b.WriteString(`\u00`)
|
||||||
|
b.WriteByte(hex[c>>4])
|
||||||
|
b.WriteByte(hex[c&0xF])
|
||||||
|
j++
|
||||||
|
i = j
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// This encodes bytes < 0x20 except for \t, \n and \r.
|
||||||
|
if c < 0x20 {
|
||||||
|
b.WriteString(s[i:j])
|
||||||
|
b.WriteString(`\u00`)
|
||||||
|
b.WriteByte(hex[c>>4])
|
||||||
|
b.WriteByte(hex[c&0xF])
|
||||||
|
j++
|
||||||
|
i = j
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
r, size := utf8.DecodeRuneInString(s[j:])
|
||||||
|
|
||||||
|
if r == utf8.RuneError && size == 1 {
|
||||||
|
b.WriteString(s[i:j])
|
||||||
|
b.WriteString(`\ufffd`)
|
||||||
|
j += size
|
||||||
|
i = j
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch r {
|
||||||
|
case '\u2028', '\u2029':
|
||||||
|
// U+2028 is LINE SEPARATOR.
|
||||||
|
// U+2029 is PARAGRAPH SEPARATOR.
|
||||||
|
// They are both technically valid characters in JSON strings,
|
||||||
|
// but don't work in JSONP, which has to be evaluated as JavaScript,
|
||||||
|
// and can lead to security holes there. It is valid JSON to
|
||||||
|
// escape them, so we do so unconditionally.
|
||||||
|
// See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
|
||||||
|
b.WriteString(s[i:j])
|
||||||
|
b.WriteString(`\u202`)
|
||||||
|
b.WriteByte(hex[r&0xF])
|
||||||
|
j += size
|
||||||
|
i = j
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
j += size
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteString(s[i:])
|
||||||
|
b.WriteByte('"')
|
||||||
|
return b.String()
|
||||||
|
}
|
29
internal/msg/element_test.go
Normal file
29
internal/msg/element_test.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package msg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func jsonMarshal(s string) string {
|
||||||
|
b, err := json.Marshal(s)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestQuoteJSON(t *testing.T) {
|
||||||
|
testcase := []string{
|
||||||
|
"\u0005", // issue 1773
|
||||||
|
"\v",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, input := range testcase {
|
||||||
|
got := QuoteJSON(input)
|
||||||
|
expected := jsonMarshal(input)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("want %v but got %v", expected, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
internal/msg/local.go
Normal file
44
internal/msg/local.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package msg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/Mrs4s/MiraiGo/message"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Poke 拍一拍
|
||||||
|
type Poke struct {
|
||||||
|
Target int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type 获取元素类型ID
|
||||||
|
func (e *Poke) Type() message.ElementType {
|
||||||
|
// Make message.IMessageElement Happy
|
||||||
|
return message.At
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocalImage 本地图片
|
||||||
|
type LocalImage struct {
|
||||||
|
Stream io.ReadSeeker
|
||||||
|
File string
|
||||||
|
URL string
|
||||||
|
|
||||||
|
Flash bool
|
||||||
|
EffectID int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type implements the message.IMessageElement.
|
||||||
|
func (e *LocalImage) Type() message.ElementType {
|
||||||
|
return message.Image
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocalVideo 本地视频
|
||||||
|
type LocalVideo struct {
|
||||||
|
File string
|
||||||
|
Thumb io.ReadSeeker
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type impl message.IMessageElement
|
||||||
|
func (e *LocalVideo) Type() message.ElementType {
|
||||||
|
return message.Video
|
||||||
|
}
|
104
internal/msg/parse.go
Normal file
104
internal/msg/parse.go
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
package msg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParseObject 将消息JSON对象转为消息元素数组
|
||||||
|
func ParseObject(m gjson.Result) (r []Element) {
|
||||||
|
convert := func(e gjson.Result) {
|
||||||
|
var elem Element
|
||||||
|
elem.Type = e.Get("type").Str
|
||||||
|
e.Get("data").ForEach(func(key, value gjson.Result) bool {
|
||||||
|
pair := Pair{K: key.Str, V: value.String()}
|
||||||
|
elem.Data = append(elem.Data, pair)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
r = append(r, elem)
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.IsArray() {
|
||||||
|
m.ForEach(func(_, e gjson.Result) bool {
|
||||||
|
convert(e)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if m.IsObject() {
|
||||||
|
convert(m)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func text(txt string) Element {
|
||||||
|
return Element{
|
||||||
|
Type: "text",
|
||||||
|
Data: []Pair{
|
||||||
|
{
|
||||||
|
K: "text",
|
||||||
|
V: txt,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseString 将字符串(CQ码)转为消息元素数组
|
||||||
|
func ParseString(raw string) (r []Element) {
|
||||||
|
var elem Element
|
||||||
|
for raw != "" {
|
||||||
|
i := 0
|
||||||
|
for i < len(raw) && !(raw[i] == '[' && i+4 < len(raw) && raw[i:i+4] == "[CQ:") {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i > 0 {
|
||||||
|
r = append(r, text(UnescapeText(raw[:i])))
|
||||||
|
}
|
||||||
|
|
||||||
|
if i+4 > len(raw) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
raw = raw[i+4:] // skip "[CQ:"
|
||||||
|
i = 0
|
||||||
|
for i < len(raw) && raw[i] != ',' && raw[i] != ']' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i+1 > len(raw) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
elem.Type = raw[:i]
|
||||||
|
elem.Data = nil // reset data
|
||||||
|
raw = raw[i:]
|
||||||
|
i = 0
|
||||||
|
for {
|
||||||
|
if raw[0] == ']' {
|
||||||
|
r = append(r, elem)
|
||||||
|
raw = raw[1:]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
raw = raw[1:]
|
||||||
|
|
||||||
|
for i < len(raw) && raw[i] != '=' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i+1 > len(raw) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
key := raw[:i]
|
||||||
|
raw = raw[i+1:] // skip "="
|
||||||
|
i = 0
|
||||||
|
for i < len(raw) && raw[i] != ',' && raw[i] != ']' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
if i+1 > len(raw) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
elem.Data = append(elem.Data, Pair{
|
||||||
|
K: key,
|
||||||
|
V: UnescapeValue(raw[:i]),
|
||||||
|
})
|
||||||
|
raw = raw[i:]
|
||||||
|
i = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
@ -1,22 +1,18 @@
|
|||||||
package coolq
|
package msg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/Mrs4s/MiraiGo/message"
|
|
||||||
"github.com/Mrs4s/MiraiGo/utils"
|
"github.com/Mrs4s/MiraiGo/utils"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
|
|
||||||
"github.com/Mrs4s/go-cqhttp/coolq/cqcode"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var bot = CQBot{}
|
func TestParseString(t *testing.T) {
|
||||||
|
// TODO: add more text
|
||||||
func TestCQBot_ConvertStringMessage(t *testing.T) {
|
for _, v := range ParseString(`[CQ:face,id=115,text=111][CQ:face,id=217]] [CQ:text,text=123] [`) {
|
||||||
for _, v := range bot.ConvertStringMessage(`[CQ:face,id=115,text=111][CQ:face,id=217]] [CQ:text,text=123] [`, message.SourcePrivate) {
|
|
||||||
fmt.Println(v)
|
fmt.Println(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -26,17 +22,18 @@ var (
|
|||||||
benchArray = gjson.Parse(`[{"type":"text","data":{"text":"asdfqwerqwerqwer"}},{"type":"face","data":{"id":"115","text":"111"}},{"type":"text","data":{"text":"asdfasdfasdfasdfasdfasdfasd"}},{"type":"face","data":{"id":"217"}},{"type":"text","data":{"text":"] "}},{"type":"text","data":{"text":"123"}},{"type":"text","data":{"text":" ["}}]`)
|
benchArray = gjson.Parse(`[{"type":"text","data":{"text":"asdfqwerqwerqwer"}},{"type":"face","data":{"id":"115","text":"111"}},{"type":"text","data":{"text":"asdfasdfasdfasdfasdfasdfasd"}},{"type":"face","data":{"id":"217"}},{"type":"text","data":{"text":"] "}},{"type":"text","data":{"text":"123"}},{"type":"text","data":{"text":" ["}}]`)
|
||||||
)
|
)
|
||||||
|
|
||||||
func BenchmarkCQBot_ConvertStringMessage(b *testing.B) {
|
func BenchmarkParseString(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
bot.ConvertStringMessage(bench, message.SourcePrivate)
|
ParseString(bench)
|
||||||
}
|
}
|
||||||
b.SetBytes(int64(len(bench)))
|
b.SetBytes(int64(len(bench)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkCQBot_ConvertObjectMessage(b *testing.B) {
|
func BenchmarkParseObject(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
bot.ConvertObjectMessage(benchArray, message.SourcePrivate)
|
ParseObject(benchArray)
|
||||||
}
|
}
|
||||||
|
b.SetBytes(int64(len(benchArray.Raw)))
|
||||||
}
|
}
|
||||||
|
|
||||||
const bText = `123456789[]&987654321[]&987654321[]&987654321[]&987654321[]&987654321[]&`
|
const bText = `123456789[]&987654321[]&987654321[]&987654321[]&987654321[]&987654321[]&`
|
||||||
@ -44,16 +41,7 @@ const bText = `123456789[]&987654321[]&987654321[]&987654321[]&987654321[]&98765
|
|||||||
func BenchmarkCQCodeEscapeText(b *testing.B) {
|
func BenchmarkCQCodeEscapeText(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
ret := bText
|
ret := bText
|
||||||
cqcode.EscapeText(ret)
|
EscapeText(ret)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkCQCodeEscapeTextBefore(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
ret := bText
|
|
||||||
ret = strings.ReplaceAll(ret, "&", "&")
|
|
||||||
ret = strings.ReplaceAll(ret, "[", "[")
|
|
||||||
strings.ReplaceAll(ret, "]", "]")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,6 +52,6 @@ func TestCQCodeEscapeText(t *testing.T) {
|
|||||||
ret = strings.ReplaceAll(ret, "&", "&")
|
ret = strings.ReplaceAll(ret, "&", "&")
|
||||||
ret = strings.ReplaceAll(ret, "[", "[")
|
ret = strings.ReplaceAll(ret, "[", "[")
|
||||||
ret = strings.ReplaceAll(ret, "]", "]")
|
ret = strings.ReplaceAll(ret, "]", "]")
|
||||||
assert.Equal(t, ret, cqcode.EscapeText(rs))
|
assert.Equal(t, ret, EscapeText(rs))
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -19,7 +19,7 @@ import (
|
|||||||
// type gjson.True or gjson.False
|
// type gjson.True or gjson.False
|
||||||
//
|
//
|
||||||
// type string "true","yes","1" or "false","no","0" (case insensitive)
|
// type string "true","yes","1" or "false","no","0" (case insensitive)
|
||||||
func EnsureBool(p interface{}, defaultVal bool) bool {
|
func EnsureBool(p any, defaultVal bool) bool {
|
||||||
var str string
|
var str string
|
||||||
if b, ok := p.(bool); ok {
|
if b, ok := p.(bool); ok {
|
||||||
return b
|
return b
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
//go:build !windows
|
//go:build !windows
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
package selfupdate
|
package selfupdate
|
||||||
|
|
||||||
|
10
main.go
10
main.go
@ -3,6 +3,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/Mrs4s/go-cqhttp/cmd/gocq"
|
"github.com/Mrs4s/go-cqhttp/cmd/gocq"
|
||||||
|
"github.com/Mrs4s/go-cqhttp/global/terminal"
|
||||||
|
|
||||||
_ "github.com/Mrs4s/go-cqhttp/db/leveldb" // leveldb 数据库支持
|
_ "github.com/Mrs4s/go-cqhttp/db/leveldb" // leveldb 数据库支持
|
||||||
_ "github.com/Mrs4s/go-cqhttp/modules/silk" // silk编码模块
|
_ "github.com/Mrs4s/go-cqhttp/modules/silk" // silk编码模块
|
||||||
@ -13,5 +14,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
gocq.Main()
|
terminal.SetTitle()
|
||||||
|
gocq.InitBase()
|
||||||
|
gocq.PrepareData()
|
||||||
|
gocq.LoginInteract()
|
||||||
|
_ = terminal.DisableQuickEdit()
|
||||||
|
_ = terminal.EnableVT100()
|
||||||
|
gocq.WaitSignal()
|
||||||
|
_ = terminal.RestoreInputMode()
|
||||||
}
|
}
|
||||||
|
@ -5,19 +5,82 @@ package api
|
|||||||
import (
|
import (
|
||||||
"github.com/Mrs4s/go-cqhttp/coolq"
|
"github.com/Mrs4s/go-cqhttp/coolq"
|
||||||
"github.com/Mrs4s/go-cqhttp/global"
|
"github.com/Mrs4s/go-cqhttp/global"
|
||||||
|
"github.com/Mrs4s/go-cqhttp/pkg/onebot"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Caller) call(action string, p Getter) global.MSG {
|
func (c *Caller) call(action string, spec *onebot.Spec, p Getter) global.MSG {
|
||||||
|
if spec.Version == 11 {
|
||||||
|
switch action {
|
||||||
|
case ".handle_quick_operation":
|
||||||
|
p0 := p.Get("context")
|
||||||
|
p1 := p.Get("operation")
|
||||||
|
return c.bot.CQHandleQuickOperation(p0, p1)
|
||||||
|
case "can_send_image":
|
||||||
|
return c.bot.CQCanSendImage()
|
||||||
|
case "can_send_record":
|
||||||
|
return c.bot.CQCanSendRecord()
|
||||||
|
case "get_login_info":
|
||||||
|
return c.bot.CQGetLoginInfo()
|
||||||
|
case "get_stranger_info":
|
||||||
|
p0 := p.Get("user_id").Int()
|
||||||
|
return c.bot.CQGetStrangerInfo(p0)
|
||||||
|
case "get_version_info":
|
||||||
|
return c.bot.CQGetVersionInfo()
|
||||||
|
case "send_forward_msg":
|
||||||
|
p0 := p.Get("group_id").Int()
|
||||||
|
p1 := p.Get("user_id").Int()
|
||||||
|
p2 := p.Get("messages")
|
||||||
|
p3 := p.Get("message_type").String()
|
||||||
|
return c.bot.CQSendForwardMessage(p0, p1, p2, p3)
|
||||||
|
case "send_group_forward_msg":
|
||||||
|
p0 := p.Get("group_id").Int()
|
||||||
|
p1 := p.Get("messages")
|
||||||
|
return c.bot.CQSendGroupForwardMessage(p0, p1)
|
||||||
|
case "send_group_msg":
|
||||||
|
p0 := p.Get("group_id").Int()
|
||||||
|
p1 := p.Get("message")
|
||||||
|
p2 := p.Get("auto_escape").Bool()
|
||||||
|
return c.bot.CQSendGroupMessage(p0, p1, p2)
|
||||||
|
case "send_msg":
|
||||||
|
p0 := p.Get("group_id").Int()
|
||||||
|
p1 := p.Get("user_id").Int()
|
||||||
|
p2 := p.Get("message")
|
||||||
|
p3 := p.Get("message_type").String()
|
||||||
|
p4 := p.Get("auto_escape").Bool()
|
||||||
|
return c.bot.CQSendMessage(p0, p1, p2, p3, p4)
|
||||||
|
case "send_private_forward_msg":
|
||||||
|
p0 := p.Get("user_id").Int()
|
||||||
|
p1 := p.Get("messages")
|
||||||
|
return c.bot.CQSendPrivateForwardMessage(p0, p1)
|
||||||
|
case "send_private_msg":
|
||||||
|
p0 := p.Get("user_id").Int()
|
||||||
|
p1 := p.Get("group_id").Int()
|
||||||
|
p2 := p.Get("message")
|
||||||
|
p3 := p.Get("auto_escape").Bool()
|
||||||
|
return c.bot.CQSendPrivateMessage(p0, p1, p2, p3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if spec.Version == 12 {
|
||||||
|
switch action {
|
||||||
|
case "get_self_info":
|
||||||
|
return c.bot.CQGetLoginInfo()
|
||||||
|
case "get_user_info":
|
||||||
|
p0 := p.Get("user_id").Int()
|
||||||
|
return c.bot.CQGetStrangerInfo(p0)
|
||||||
|
case "get_version":
|
||||||
|
return c.bot.CQGetVersion()
|
||||||
|
case "send_message":
|
||||||
|
p0 := p.Get("group_id").String()
|
||||||
|
p1 := p.Get("user_id").String()
|
||||||
|
p2 := p.Get("detail_type").String()
|
||||||
|
p3 := p.Get("message")
|
||||||
|
return c.bot.CQSendMessageV12(p0, p1, p2, p3)
|
||||||
|
}
|
||||||
|
}
|
||||||
switch action {
|
switch action {
|
||||||
default:
|
|
||||||
return coolq.Failed(404, "API_NOT_FOUND", "API不存在")
|
|
||||||
case ".get_word_slices":
|
case ".get_word_slices":
|
||||||
p0 := p.Get("content").String()
|
p0 := p.Get("content").String()
|
||||||
return c.bot.CQGetWordSlices(p0)
|
return c.bot.CQGetWordSlices(p0)
|
||||||
case ".handle_quick_operation":
|
|
||||||
p0 := p.Get("context")
|
|
||||||
p1 := p.Get("operation")
|
|
||||||
return c.bot.CQHandleQuickOperation(p0, p1)
|
|
||||||
case ".ocr_image", "ocr_image":
|
case ".ocr_image", "ocr_image":
|
||||||
p0 := p.Get("image").String()
|
p0 := p.Get("image").String()
|
||||||
return c.bot.CQOcrImage(p0)
|
return c.bot.CQOcrImage(p0)
|
||||||
@ -40,10 +103,6 @@ func (c *Caller) call(action string, p Getter) global.MSG {
|
|||||||
p0 := p.Get("model").String()
|
p0 := p.Get("model").String()
|
||||||
p1 := p.Get("model_show").String()
|
p1 := p.Get("model_show").String()
|
||||||
return c.bot.CQSetModelShow(p0, p1)
|
return c.bot.CQSetModelShow(p0, p1)
|
||||||
case "can_send_image":
|
|
||||||
return c.bot.CQCanSendImage()
|
|
||||||
case "can_send_record":
|
|
||||||
return c.bot.CQCanSendRecord()
|
|
||||||
case "check_url_safely":
|
case "check_url_safely":
|
||||||
p0 := p.Get("url").String()
|
p0 := p.Get("url").String()
|
||||||
return c.bot.CQCheckURLSafely(p0)
|
return c.bot.CQCheckURLSafely(p0)
|
||||||
@ -96,7 +155,7 @@ func (c *Caller) call(action string, p Getter) global.MSG {
|
|||||||
p0 := p.Get("[message_id,id].0").String()
|
p0 := p.Get("[message_id,id].0").String()
|
||||||
return c.bot.CQGetForwardMessage(p0)
|
return c.bot.CQGetForwardMessage(p0)
|
||||||
case "get_friend_list":
|
case "get_friend_list":
|
||||||
return c.bot.CQGetFriendList()
|
return c.bot.CQGetFriendList(spec)
|
||||||
case "get_group_at_all_remain":
|
case "get_group_at_all_remain":
|
||||||
p0 := p.Get("group_id").Int()
|
p0 := p.Get("group_id").Int()
|
||||||
return c.bot.CQGetAtAllRemain(p0)
|
return c.bot.CQGetAtAllRemain(p0)
|
||||||
@ -119,10 +178,10 @@ func (c *Caller) call(action string, p Getter) global.MSG {
|
|||||||
case "get_group_info":
|
case "get_group_info":
|
||||||
p0 := p.Get("group_id").Int()
|
p0 := p.Get("group_id").Int()
|
||||||
p1 := p.Get("no_cache").Bool()
|
p1 := p.Get("no_cache").Bool()
|
||||||
return c.bot.CQGetGroupInfo(p0, p1)
|
return c.bot.CQGetGroupInfo(p0, p1, spec)
|
||||||
case "get_group_list":
|
case "get_group_list":
|
||||||
p0 := p.Get("no_cache").Bool()
|
p0 := p.Get("no_cache").Bool()
|
||||||
return c.bot.CQGetGroupList(p0)
|
return c.bot.CQGetGroupList(p0, spec)
|
||||||
case "get_group_member_info":
|
case "get_group_member_info":
|
||||||
p0 := p.Get("group_id").Int()
|
p0 := p.Get("group_id").Int()
|
||||||
p1 := p.Get("user_id").Int()
|
p1 := p.Get("user_id").Int()
|
||||||
@ -170,8 +229,6 @@ func (c *Caller) call(action string, p Getter) global.MSG {
|
|||||||
case "get_image":
|
case "get_image":
|
||||||
p0 := p.Get("file").String()
|
p0 := p.Get("file").String()
|
||||||
return c.bot.CQGetImage(p0)
|
return c.bot.CQGetImage(p0)
|
||||||
case "get_login_info":
|
|
||||||
return c.bot.CQGetLoginInfo()
|
|
||||||
case "get_msg":
|
case "get_msg":
|
||||||
p0 := int32(p.Get("message_id").Int())
|
p0 := int32(p.Get("message_id").Int())
|
||||||
return c.bot.CQGetMessage(p0)
|
return c.bot.CQGetMessage(p0)
|
||||||
@ -179,18 +236,15 @@ func (c *Caller) call(action string, p Getter) global.MSG {
|
|||||||
p0 := p.Get("no_cache").Bool()
|
p0 := p.Get("no_cache").Bool()
|
||||||
return c.bot.CQGetOnlineClients(p0)
|
return c.bot.CQGetOnlineClients(p0)
|
||||||
case "get_status":
|
case "get_status":
|
||||||
return c.bot.CQGetStatus()
|
return c.bot.CQGetStatus(spec)
|
||||||
case "get_stranger_info":
|
case "get_supported_actions":
|
||||||
p0 := p.Get("user_id").Int()
|
return c.bot.CQGetSupportedActions(spec)
|
||||||
return c.bot.CQGetStrangerInfo(p0)
|
|
||||||
case "get_topic_channel_feeds":
|
case "get_topic_channel_feeds":
|
||||||
p0 := p.Get("guild_id").Uint()
|
p0 := p.Get("guild_id").Uint()
|
||||||
p1 := p.Get("channel_id").Uint()
|
p1 := p.Get("channel_id").Uint()
|
||||||
return c.bot.CQGetTopicChannelFeeds(p0, p1)
|
return c.bot.CQGetTopicChannelFeeds(p0, p1)
|
||||||
case "get_unidirectional_friend_list":
|
case "get_unidirectional_friend_list":
|
||||||
return c.bot.CQGetUnidirectionalFriendList()
|
return c.bot.CQGetUnidirectionalFriendList()
|
||||||
case "get_version_info":
|
|
||||||
return c.bot.CQGetVersionInfo()
|
|
||||||
case "mark_msg_as_read":
|
case "mark_msg_as_read":
|
||||||
p0 := int32(p.Get("message_id").Int())
|
p0 := int32(p.Get("message_id").Int())
|
||||||
return c.bot.CQMarkMessageAsRead(p0)
|
return c.bot.CQMarkMessageAsRead(p0)
|
||||||
@ -199,21 +253,6 @@ func (c *Caller) call(action string, p Getter) global.MSG {
|
|||||||
case "reload_event_filter":
|
case "reload_event_filter":
|
||||||
p0 := p.Get("file").String()
|
p0 := p.Get("file").String()
|
||||||
return c.bot.CQReloadEventFilter(p0)
|
return c.bot.CQReloadEventFilter(p0)
|
||||||
case "send_forward_msg":
|
|
||||||
p0 := p.Get("group_id").Int()
|
|
||||||
p1 := p.Get("user_id").Int()
|
|
||||||
p2 := p.Get("messages")
|
|
||||||
p3 := p.Get("message_type").String()
|
|
||||||
return c.bot.CQSendForwardMessage(p0, p1, p2, p3)
|
|
||||||
case "send_group_forward_msg":
|
|
||||||
p0 := p.Get("group_id").Int()
|
|
||||||
p1 := p.Get("messages")
|
|
||||||
return c.bot.CQSendGroupForwardMessage(p0, p1)
|
|
||||||
case "send_group_msg":
|
|
||||||
p0 := p.Get("group_id").Int()
|
|
||||||
p1 := p.Get("message")
|
|
||||||
p2 := p.Get("auto_escape").Bool()
|
|
||||||
return c.bot.CQSendGroupMessage(p0, p1, p2)
|
|
||||||
case "send_group_sign":
|
case "send_group_sign":
|
||||||
p0 := p.Get("group_id").Int()
|
p0 := p.Get("group_id").Int()
|
||||||
return c.bot.CQSendGroupSign(p0)
|
return c.bot.CQSendGroupSign(p0)
|
||||||
@ -223,23 +262,6 @@ func (c *Caller) call(action string, p Getter) global.MSG {
|
|||||||
p2 := p.Get("message")
|
p2 := p.Get("message")
|
||||||
p3 := p.Get("auto_escape").Bool()
|
p3 := p.Get("auto_escape").Bool()
|
||||||
return c.bot.CQSendGuildChannelMessage(p0, p1, p2, p3)
|
return c.bot.CQSendGuildChannelMessage(p0, p1, p2, p3)
|
||||||
case "send_msg":
|
|
||||||
p0 := p.Get("group_id").Int()
|
|
||||||
p1 := p.Get("user_id").Int()
|
|
||||||
p2 := p.Get("message")
|
|
||||||
p3 := p.Get("message_type").String()
|
|
||||||
p4 := p.Get("auto_escape").Bool()
|
|
||||||
return c.bot.CQSendMessage(p0, p1, p2, p3, p4)
|
|
||||||
case "send_private_forward_msg":
|
|
||||||
p0 := p.Get("user_id").Int()
|
|
||||||
p1 := p.Get("messages")
|
|
||||||
return c.bot.CQSendPrivateForwardMessage(p0, p1)
|
|
||||||
case "send_private_msg":
|
|
||||||
p0 := p.Get("user_id").Int()
|
|
||||||
p1 := p.Get("group_id").Int()
|
|
||||||
p2 := p.Get("message")
|
|
||||||
p3 := p.Get("auto_escape").Bool()
|
|
||||||
return c.bot.CQSendPrivateMessage(p0, p1, p2, p3)
|
|
||||||
case "set_essence_msg":
|
case "set_essence_msg":
|
||||||
p0 := int32(p.Get("message_id").Int())
|
p0 := int32(p.Get("message_id").Int())
|
||||||
return c.bot.CQSetEssenceMessage(p0)
|
return c.bot.CQSetEssenceMessage(p0)
|
||||||
@ -267,6 +289,13 @@ func (c *Caller) call(action string, p Getter) global.MSG {
|
|||||||
p2 = pt.Bool()
|
p2 = pt.Bool()
|
||||||
}
|
}
|
||||||
return c.bot.CQSetGroupAdmin(p0, p1, p2)
|
return c.bot.CQSetGroupAdmin(p0, p1, p2)
|
||||||
|
case "set_group_anonymous":
|
||||||
|
p0 := p.Get("group_id").Int()
|
||||||
|
p1 := true
|
||||||
|
if pt := p.Get("enable"); pt.Exists() {
|
||||||
|
p1 = pt.Bool()
|
||||||
|
}
|
||||||
|
return c.bot.CQSetGroupAnonymous(p0, p1)
|
||||||
case "set_group_anonymous_ban":
|
case "set_group_anonymous_ban":
|
||||||
p0 := p.Get("group_id").Int()
|
p0 := p.Get("group_id").Int()
|
||||||
p1 := p.Get("[anonymous_flag,anonymous.flag].0").String()
|
p1 := p.Get("[anonymous_flag,anonymous.flag].0").String()
|
||||||
@ -347,4 +376,5 @@ func (c *Caller) call(action string, p Getter) global.MSG {
|
|||||||
p2 := p.Get("name").String()
|
p2 := p.Get("name").String()
|
||||||
return c.bot.CQUploadPrivateFile(p0, p1, p2)
|
return c.bot.CQUploadPrivateFile(p0, p1, p2)
|
||||||
}
|
}
|
||||||
|
return coolq.Failed(404, "API_NOT_FOUND", "API不存在")
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,10 @@ import (
|
|||||||
|
|
||||||
"github.com/Mrs4s/go-cqhttp/coolq"
|
"github.com/Mrs4s/go-cqhttp/coolq"
|
||||||
"github.com/Mrs4s/go-cqhttp/global"
|
"github.com/Mrs4s/go-cqhttp/global"
|
||||||
|
"github.com/Mrs4s/go-cqhttp/pkg/onebot"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate go run github.com/Mrs4s/go-cqhttp/cmd/api-generator -path=./../../coolq/api.go
|
//go:generate go run ./../../cmd/api-generator -pkg api -path=./../../coolq/api.go,./../../coolq/api_v12.go -o api.go
|
||||||
|
|
||||||
// Getter 参数获取
|
// Getter 参数获取
|
||||||
type Getter interface {
|
type Getter interface {
|
||||||
@ -16,7 +17,7 @@ type Getter interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handler 中间件
|
// Handler 中间件
|
||||||
type Handler func(action string, p Getter) global.MSG
|
type Handler func(action string, spe *onebot.Spec, p Getter) global.MSG
|
||||||
|
|
||||||
// Caller api route caller
|
// Caller api route caller
|
||||||
type Caller struct {
|
type Caller struct {
|
||||||
@ -25,13 +26,13 @@ type Caller struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Call specific API
|
// Call specific API
|
||||||
func (c *Caller) Call(action string, p Getter) global.MSG {
|
func (c *Caller) Call(action string, spec *onebot.Spec, p Getter) global.MSG {
|
||||||
for _, fn := range c.handlers {
|
for _, fn := range c.handlers {
|
||||||
if ret := fn(action, p); ret != nil {
|
if ret := fn(action, spec, p); ret != nil {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return c.call(action, p)
|
return c.call(action, spec, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use add handlers to the API caller
|
// Use add handlers to the API caller
|
||||||
|
@ -55,6 +55,7 @@ type Config struct {
|
|||||||
RemoveReplyAt bool `yaml:"remove-reply-at"`
|
RemoveReplyAt bool `yaml:"remove-reply-at"`
|
||||||
ExtraReplyData bool `yaml:"extra-reply-data"`
|
ExtraReplyData bool `yaml:"extra-reply-data"`
|
||||||
SkipMimeScan bool `yaml:"skip-mime-scan"`
|
SkipMimeScan bool `yaml:"skip-mime-scan"`
|
||||||
|
ConvertWebpImage bool `yaml:"convert-webp-image"`
|
||||||
} `yaml:"message"`
|
} `yaml:"message"`
|
||||||
|
|
||||||
Output struct {
|
Output struct {
|
||||||
|
@ -43,6 +43,8 @@ message:
|
|||||||
extra-reply-data: false
|
extra-reply-data: false
|
||||||
# 跳过 Mime 扫描, 忽略错误数据
|
# 跳过 Mime 扫描, 忽略错误数据
|
||||||
skip-mime-scan: false
|
skip-mime-scan: false
|
||||||
|
# 是否自动转换 WebP 图片
|
||||||
|
convert-webp-image: false
|
||||||
|
|
||||||
output:
|
output:
|
||||||
# 日志等级 trace,debug,info,warn,error
|
# 日志等级 trace,debug,info,warn,error
|
||||||
|
@ -32,6 +32,9 @@ func encode(record []byte, tempName string) (silkWav []byte, err error) {
|
|||||||
// 2.转换pcm
|
// 2.转换pcm
|
||||||
pcmPath := path.Join(silkCachePath, tempName+".pcm")
|
pcmPath := path.Join(silkCachePath, tempName+".pcm")
|
||||||
cmd := exec.Command("ffmpeg", "-i", rawPath, "-f", "s16le", "-ar", "24000", "-ac", "1", pcmPath)
|
cmd := exec.Command("ffmpeg", "-i", rawPath, "-f", "s16le", "-ar", "24000", "-ac", "1", pcmPath)
|
||||||
|
if errors.Is(cmd.Err, exec.ErrDot) {
|
||||||
|
cmd.Err = nil
|
||||||
|
}
|
||||||
if base.Debug {
|
if base.Debug {
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
41
pkg/onebot/onebot.go
Normal file
41
pkg/onebot/onebot.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package onebot
|
||||||
|
|
||||||
|
// Self 机器人自身标识
|
||||||
|
//
|
||||||
|
// https://12.onebot.dev/connect/data-protocol/basic-types/#_10
|
||||||
|
type Self struct {
|
||||||
|
Platform string `json:"platform"`
|
||||||
|
UserID string `json:"user_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request 动作请求是应用端为了主动向 OneBot 实现请求服务而发送的数据
|
||||||
|
//
|
||||||
|
// https://12.onebot.dev/connect/data-protocol/action-request/
|
||||||
|
type Request struct {
|
||||||
|
Action string // 动作名称
|
||||||
|
Params any // 动作参数
|
||||||
|
Echo any // 每次请求的唯一标识
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response 动作响应是 OneBot 实现收到应用端的动作请求并处理完毕后,发回应用端的数据
|
||||||
|
//
|
||||||
|
// https://12.onebot.dev/connect/data-protocol/action-response/
|
||||||
|
type Response struct {
|
||||||
|
Status string `json:"status"` // 执行状态,必须是 ok、failed 中的一个
|
||||||
|
Code int64 `json:"retcode"` // 返回码
|
||||||
|
Data any `json:"data"` // 响应数据
|
||||||
|
Message string `json:"message"` // 错误信息
|
||||||
|
Echo any `json:"echo"` // 动作请求中的 echo 字段值
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event 事件
|
||||||
|
//
|
||||||
|
// https://12.onebot.dev/connect/data-protocol/event/
|
||||||
|
type Event struct {
|
||||||
|
ID string
|
||||||
|
Time int64
|
||||||
|
Type string
|
||||||
|
DetailType string
|
||||||
|
SubType string
|
||||||
|
Self *Self
|
||||||
|
}
|
32
pkg/onebot/spec.go
Normal file
32
pkg/onebot/spec.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Package onebot defines onebot protocol struct and some spec info.
|
||||||
|
package onebot
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//go:generate go run ./../../cmd/api-generator -pkg onebot -path=./../../coolq/api.go,./../../coolq/api_v12.go -supported -o supported.go
|
||||||
|
|
||||||
|
// Spec OneBot Specification
|
||||||
|
type Spec struct {
|
||||||
|
Version int // must be 11 or 12
|
||||||
|
SupportedActions []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// V11 OneBot V11
|
||||||
|
var V11 = &Spec{
|
||||||
|
Version: 11,
|
||||||
|
SupportedActions: supportedV11,
|
||||||
|
}
|
||||||
|
|
||||||
|
// V12 OneBot V12
|
||||||
|
var V12 = &Spec{
|
||||||
|
Version: 12,
|
||||||
|
SupportedActions: supportedV12,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertID 根据版本转换ID
|
||||||
|
func (s *Spec) ConvertID(id any) any {
|
||||||
|
if s.Version == 12 {
|
||||||
|
return fmt.Sprint(id)
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
}
|
169
pkg/onebot/supported.go
Normal file
169
pkg/onebot/supported.go
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
// Code generated by cmd/api-generator. DO NOT EDIT.
|
||||||
|
|
||||||
|
package onebot
|
||||||
|
|
||||||
|
var supportedV11 = []string{
|
||||||
|
".get_word_slices",
|
||||||
|
".handle_quick_operation",
|
||||||
|
".ocr_image",
|
||||||
|
"ocr_image",
|
||||||
|
"_del_group_notice",
|
||||||
|
"_get_group_notice",
|
||||||
|
"_get_model_show",
|
||||||
|
"_send_group_notice",
|
||||||
|
"_set_model_show",
|
||||||
|
"can_send_image",
|
||||||
|
"can_send_record",
|
||||||
|
"check_url_safely",
|
||||||
|
"create_group_file_folder",
|
||||||
|
"create_guild_role",
|
||||||
|
"delete_essence_msg",
|
||||||
|
"delete_friend",
|
||||||
|
"delete_group_file",
|
||||||
|
"delete_group_folder",
|
||||||
|
"delete_guild_role",
|
||||||
|
"delete_msg",
|
||||||
|
"delete_unidirectional_friend",
|
||||||
|
"download_file",
|
||||||
|
"get_essence_msg_list",
|
||||||
|
"get_forward_msg",
|
||||||
|
"get_friend_list",
|
||||||
|
"get_group_at_all_remain",
|
||||||
|
"get_group_file_system_info",
|
||||||
|
"get_group_file_url",
|
||||||
|
"get_group_files_by_folder",
|
||||||
|
"get_group_honor_info",
|
||||||
|
"get_group_info",
|
||||||
|
"get_group_list",
|
||||||
|
"get_group_member_info",
|
||||||
|
"get_group_member_list",
|
||||||
|
"get_group_msg_history",
|
||||||
|
"get_group_root_files",
|
||||||
|
"get_group_system_msg",
|
||||||
|
"get_guild_channel_list",
|
||||||
|
"get_guild_list",
|
||||||
|
"get_guild_member_list",
|
||||||
|
"get_guild_member_profile",
|
||||||
|
"get_guild_meta_by_guest",
|
||||||
|
"get_guild_msg",
|
||||||
|
"get_guild_roles",
|
||||||
|
"get_guild_service_profile",
|
||||||
|
"get_image",
|
||||||
|
"get_login_info",
|
||||||
|
"get_msg",
|
||||||
|
"get_online_clients",
|
||||||
|
"get_status",
|
||||||
|
"get_stranger_info",
|
||||||
|
"get_supported_actions",
|
||||||
|
"get_topic_channel_feeds",
|
||||||
|
"get_unidirectional_friend_list",
|
||||||
|
"get_version_info",
|
||||||
|
"mark_msg_as_read",
|
||||||
|
"qidian_get_account_info",
|
||||||
|
"reload_event_filter",
|
||||||
|
"send_forward_msg",
|
||||||
|
"send_group_forward_msg",
|
||||||
|
"send_group_msg",
|
||||||
|
"send_group_sign",
|
||||||
|
"send_guild_channel_msg",
|
||||||
|
"send_msg",
|
||||||
|
"send_private_forward_msg",
|
||||||
|
"send_private_msg",
|
||||||
|
"set_essence_msg",
|
||||||
|
"set_friend_add_request",
|
||||||
|
"set_group_add_request",
|
||||||
|
"set_group_admin",
|
||||||
|
"set_group_anonymous",
|
||||||
|
"set_group_anonymous_ban",
|
||||||
|
"set_group_ban",
|
||||||
|
"set_group_card",
|
||||||
|
"set_group_kick",
|
||||||
|
"set_group_leave",
|
||||||
|
"set_group_name",
|
||||||
|
"set_group_portrait",
|
||||||
|
"set_group_special_title",
|
||||||
|
"set_group_whole_ban",
|
||||||
|
"set_guild_member_role",
|
||||||
|
"set_qq_profile",
|
||||||
|
"update_guild_role",
|
||||||
|
"upload_group_file",
|
||||||
|
"upload_private_file",
|
||||||
|
}
|
||||||
|
|
||||||
|
var supportedV12 = []string{
|
||||||
|
".get_word_slices",
|
||||||
|
".ocr_image",
|
||||||
|
"ocr_image",
|
||||||
|
"_del_group_notice",
|
||||||
|
"_get_group_notice",
|
||||||
|
"_get_model_show",
|
||||||
|
"_send_group_notice",
|
||||||
|
"_set_model_show",
|
||||||
|
"check_url_safely",
|
||||||
|
"create_group_file_folder",
|
||||||
|
"create_guild_role",
|
||||||
|
"delete_essence_msg",
|
||||||
|
"delete_friend",
|
||||||
|
"delete_group_file",
|
||||||
|
"delete_group_folder",
|
||||||
|
"delete_guild_role",
|
||||||
|
"delete_msg",
|
||||||
|
"delete_unidirectional_friend",
|
||||||
|
"download_file",
|
||||||
|
"get_essence_msg_list",
|
||||||
|
"get_forward_msg",
|
||||||
|
"get_friend_list",
|
||||||
|
"get_group_at_all_remain",
|
||||||
|
"get_group_file_system_info",
|
||||||
|
"get_group_file_url",
|
||||||
|
"get_group_files_by_folder",
|
||||||
|
"get_group_honor_info",
|
||||||
|
"get_group_info",
|
||||||
|
"get_group_list",
|
||||||
|
"get_group_member_info",
|
||||||
|
"get_group_member_list",
|
||||||
|
"get_group_msg_history",
|
||||||
|
"get_group_root_files",
|
||||||
|
"get_group_system_msg",
|
||||||
|
"get_guild_channel_list",
|
||||||
|
"get_guild_list",
|
||||||
|
"get_guild_member_list",
|
||||||
|
"get_guild_member_profile",
|
||||||
|
"get_guild_meta_by_guest",
|
||||||
|
"get_guild_msg",
|
||||||
|
"get_guild_roles",
|
||||||
|
"get_guild_service_profile",
|
||||||
|
"get_image",
|
||||||
|
"get_self_info",
|
||||||
|
"get_msg",
|
||||||
|
"get_online_clients",
|
||||||
|
"get_status",
|
||||||
|
"get_user_info",
|
||||||
|
"get_supported_actions",
|
||||||
|
"get_topic_channel_feeds",
|
||||||
|
"get_unidirectional_friend_list",
|
||||||
|
"mark_msg_as_read",
|
||||||
|
"qidian_get_account_info",
|
||||||
|
"reload_event_filter",
|
||||||
|
"send_group_sign",
|
||||||
|
"send_guild_channel_msg",
|
||||||
|
"set_essence_msg",
|
||||||
|
"set_friend_add_request",
|
||||||
|
"set_group_add_request",
|
||||||
|
"set_group_admin",
|
||||||
|
"set_group_anonymous",
|
||||||
|
"set_group_anonymous_ban",
|
||||||
|
"set_group_ban",
|
||||||
|
"set_group_card",
|
||||||
|
"set_group_kick",
|
||||||
|
"set_group_leave",
|
||||||
|
"set_group_name",
|
||||||
|
"set_group_portrait",
|
||||||
|
"set_group_special_title",
|
||||||
|
"set_group_whole_ban",
|
||||||
|
"set_guild_member_role",
|
||||||
|
"set_qq_profile",
|
||||||
|
"update_guild_role",
|
||||||
|
"upload_group_file",
|
||||||
|
"upload_private_file",
|
||||||
|
}
|
@ -29,11 +29,13 @@ import (
|
|||||||
"github.com/Mrs4s/go-cqhttp/modules/api"
|
"github.com/Mrs4s/go-cqhttp/modules/api"
|
||||||
"github.com/Mrs4s/go-cqhttp/modules/config"
|
"github.com/Mrs4s/go-cqhttp/modules/config"
|
||||||
"github.com/Mrs4s/go-cqhttp/modules/filter"
|
"github.com/Mrs4s/go-cqhttp/modules/filter"
|
||||||
|
"github.com/Mrs4s/go-cqhttp/pkg/onebot"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HTTPServer HTTP通信相关配置
|
// HTTPServer HTTP通信相关配置
|
||||||
type HTTPServer struct {
|
type HTTPServer struct {
|
||||||
Disabled bool `yaml:"disabled"`
|
Disabled bool `yaml:"disabled"`
|
||||||
|
Version uint16 `yaml:"version"`
|
||||||
Address string `yaml:"address"`
|
Address string `yaml:"address"`
|
||||||
Host string `yaml:"host"`
|
Host string `yaml:"host"`
|
||||||
Port int `yaml:"port"`
|
Port int `yaml:"port"`
|
||||||
@ -57,6 +59,7 @@ type httpServerPost struct {
|
|||||||
type httpServer struct {
|
type httpServer struct {
|
||||||
api *api.Caller
|
api *api.Caller
|
||||||
accessToken string
|
accessToken string
|
||||||
|
spec *onebot.Spec // onebot spec
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTPClient 反向HTTP上报客户端
|
// HTTPClient 反向HTTP上报客户端
|
||||||
@ -81,6 +84,7 @@ type httpCtx struct {
|
|||||||
const httpDefault = `
|
const httpDefault = `
|
||||||
- http: # HTTP 通信设置
|
- http: # HTTP 通信设置
|
||||||
address: 0.0.0.0:5700 # HTTP监听地址
|
address: 0.0.0.0:5700 # HTTP监听地址
|
||||||
|
version: 11 # OneBot协议版本, 支持 11/12
|
||||||
timeout: 5 # 反向 HTTP 超时时间, 单位秒,<5 时将被忽略
|
timeout: 5 # 反向 HTTP 超时时间, 单位秒,<5 时将被忽略
|
||||||
long-polling: # 长轮询拓展
|
long-polling: # 长轮询拓展
|
||||||
enabled: false # 是否开启
|
enabled: false # 是否开启
|
||||||
@ -104,31 +108,35 @@ func init() {
|
|||||||
|
|
||||||
var joinQuery = regexp.MustCompile(`\[(.+?),(.+?)]\.0`)
|
var joinQuery = regexp.MustCompile(`\[(.+?),(.+?)]\.0`)
|
||||||
|
|
||||||
func (h *httpCtx) get(s string, join bool) gjson.Result {
|
func mayJSONParam(p string) bool {
|
||||||
|
if strings.HasPrefix(p, "{") || strings.HasPrefix(p, "[") {
|
||||||
|
return gjson.Valid(p)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *httpCtx) get(pattern string, join bool) gjson.Result {
|
||||||
// support gjson advanced syntax:
|
// support gjson advanced syntax:
|
||||||
// h.Get("[a,b].0") see usage in http_test.go
|
// h.Get("[a,b].0") see usage in http_test.go. See issue #1241, #1325.
|
||||||
if join && joinQuery.MatchString(s) {
|
if join && strings.HasPrefix(pattern, "[") && joinQuery.MatchString(pattern) {
|
||||||
matched := joinQuery.FindStringSubmatch(s)
|
matched := joinQuery.FindStringSubmatch(pattern)
|
||||||
if r := h.get(matched[1], false); r.Exists() {
|
if r := h.get(matched[1], false); r.Exists() {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
return h.get(matched[2], false)
|
return h.get(matched[2], false)
|
||||||
}
|
}
|
||||||
|
|
||||||
validJSONParam := func(p string) bool {
|
|
||||||
return (strings.HasPrefix(p, "{") || strings.HasPrefix(p, "[")) && gjson.Valid(p)
|
|
||||||
}
|
|
||||||
if h.postForm != nil {
|
if h.postForm != nil {
|
||||||
if form := h.postForm.Get(s); form != "" {
|
if form := h.postForm.Get(pattern); form != "" {
|
||||||
if validJSONParam(form) {
|
if mayJSONParam(form) {
|
||||||
return gjson.Result{Type: gjson.JSON, Raw: form}
|
return gjson.Result{Type: gjson.JSON, Raw: form}
|
||||||
}
|
}
|
||||||
return gjson.Result{Type: gjson.String, Str: form}
|
return gjson.Result{Type: gjson.String, Str: form}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if h.query != nil {
|
if h.query != nil {
|
||||||
if query := h.query.Get(s); query != "" {
|
if query := h.query.Get(pattern); query != "" {
|
||||||
if validJSONParam(query) {
|
if mayJSONParam(query) {
|
||||||
return gjson.Result{Type: gjson.JSON, Raw: query}
|
return gjson.Result{Type: gjson.JSON, Raw: query}
|
||||||
}
|
}
|
||||||
return gjson.Result{Type: gjson.String, Str: query}
|
return gjson.Result{Type: gjson.String, Str: query}
|
||||||
@ -150,6 +158,13 @@ func (s *httpServer) ServeHTTP(writer http.ResponseWriter, request *http.Request
|
|||||||
contentType := request.Header.Get("Content-Type")
|
contentType := request.Header.Get("Content-Type")
|
||||||
switch request.Method {
|
switch request.Method {
|
||||||
case http.MethodPost:
|
case http.MethodPost:
|
||||||
|
// todo: msg pack
|
||||||
|
if s.spec.Version == 12 && strings.Contains(contentType, "application/msgpack") {
|
||||||
|
log.Warnf("请求 %v 数据类型暂不支持: MsgPack", request.RequestURI)
|
||||||
|
writer.WriteHeader(http.StatusUnsupportedMediaType)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if strings.Contains(contentType, "application/json") {
|
if strings.Contains(contentType, "application/json") {
|
||||||
body, err := io.ReadAll(request.Body)
|
body, err := io.ReadAll(request.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -190,12 +205,12 @@ func (s *httpServer) ServeHTTP(writer http.ResponseWriter, request *http.Request
|
|||||||
if request.URL.Path == "/" {
|
if request.URL.Path == "/" {
|
||||||
action := strings.TrimSuffix(ctx.Get("action").Str, "_async")
|
action := strings.TrimSuffix(ctx.Get("action").Str, "_async")
|
||||||
log.Debugf("HTTPServer接收到API调用: %v", action)
|
log.Debugf("HTTPServer接收到API调用: %v", action)
|
||||||
response = s.api.Call(action, ctx.Get("params"))
|
response = s.api.Call(action, s.spec, ctx.Get("params"))
|
||||||
} else {
|
} else {
|
||||||
action := strings.TrimPrefix(request.URL.Path, "/")
|
action := strings.TrimPrefix(request.URL.Path, "/")
|
||||||
action = strings.TrimSuffix(action, "_async")
|
action = strings.TrimSuffix(action, "_async")
|
||||||
log.Debugf("HTTPServer接收到API调用: %v", action)
|
log.Debugf("HTTPServer接收到API调用: %v", action)
|
||||||
response = s.api.Call(action, &ctx)
|
response = s.api.Call(action, s.spec, &ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
@ -245,9 +260,15 @@ func runHTTP(bot *coolq.CQBot, node yaml.Node) {
|
|||||||
case conf.Disabled:
|
case conf.Disabled:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
network, addr := "tcp", conf.Address
|
network, addr := "tcp", conf.Address
|
||||||
s := &httpServer{accessToken: conf.AccessToken}
|
s := &httpServer{accessToken: conf.AccessToken}
|
||||||
|
switch conf.Version {
|
||||||
|
default:
|
||||||
|
// default v11
|
||||||
|
s.spec = onebot.V11
|
||||||
|
case 12:
|
||||||
|
s.spec = onebot.V12
|
||||||
|
}
|
||||||
switch {
|
switch {
|
||||||
case conf.Address != "":
|
case conf.Address != "":
|
||||||
uri, err := url.Parse(conf.Address)
|
uri, err := url.Parse(conf.Address)
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/Mrs4s/go-cqhttp/coolq"
|
"github.com/Mrs4s/go-cqhttp/coolq"
|
||||||
"github.com/Mrs4s/go-cqhttp/global"
|
"github.com/Mrs4s/go-cqhttp/global"
|
||||||
"github.com/Mrs4s/go-cqhttp/modules/api"
|
"github.com/Mrs4s/go-cqhttp/modules/api"
|
||||||
|
"github.com/Mrs4s/go-cqhttp/pkg/onebot"
|
||||||
|
|
||||||
"golang.org/x/time/rate"
|
"golang.org/x/time/rate"
|
||||||
)
|
)
|
||||||
@ -26,7 +27,7 @@ type MiddleWares struct {
|
|||||||
|
|
||||||
func rateLimit(frequency float64, bucketSize int) api.Handler {
|
func rateLimit(frequency float64, bucketSize int) api.Handler {
|
||||||
limiter := rate.NewLimiter(rate.Limit(frequency), bucketSize)
|
limiter := rate.NewLimiter(rate.Limit(frequency), bucketSize)
|
||||||
return func(_ string, _ api.Getter) global.MSG {
|
return func(_ string, _ *onebot.Spec, _ api.Getter) global.MSG {
|
||||||
_ = limiter.Wait(context.Background())
|
_ = limiter.Wait(context.Background())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -45,12 +46,15 @@ func longPolling(bot *coolq.CQBot, maxSize int) api.Handler {
|
|||||||
}
|
}
|
||||||
cond.Signal()
|
cond.Signal()
|
||||||
})
|
})
|
||||||
return func(action string, p api.Getter) global.MSG {
|
return func(action string, spec *onebot.Spec, p api.Getter) global.MSG {
|
||||||
if action != "get_updates" {
|
switch {
|
||||||
|
case spec.Version == 11 && action == "get_updates": // ok
|
||||||
|
case spec.Version == 12 && action == "get_latest_events": // ok
|
||||||
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
ch = make(chan []interface{})
|
ch = make(chan []any)
|
||||||
timeout = time.Duration(p.Get("timeout").Int()) * time.Second
|
timeout = time.Duration(p.Get("timeout").Int()) * time.Second
|
||||||
)
|
)
|
||||||
go func() {
|
go func() {
|
||||||
@ -63,7 +67,7 @@ func longPolling(bot *coolq.CQBot, maxSize int) api.Handler {
|
|||||||
if limit <= 0 || queue.Len() < limit {
|
if limit <= 0 || queue.Len() < limit {
|
||||||
limit = queue.Len()
|
limit = queue.Len()
|
||||||
}
|
}
|
||||||
ret := make([]interface{}, limit)
|
ret := make([]any, limit)
|
||||||
elem := queue.Front()
|
elem := queue.Front()
|
||||||
for i := 0; i < limit; i++ {
|
for i := 0; i < limit; i++ {
|
||||||
ret[i] = elem.Value
|
ret[i] = elem.Value
|
||||||
@ -81,7 +85,7 @@ func longPolling(bot *coolq.CQBot, maxSize int) api.Handler {
|
|||||||
if timeout != 0 {
|
if timeout != 0 {
|
||||||
select {
|
select {
|
||||||
case <-time.After(timeout):
|
case <-time.After(timeout):
|
||||||
return coolq.OK([]interface{}{})
|
return coolq.OK([]any{})
|
||||||
case ret := <-ch:
|
case ret := <-ch:
|
||||||
return coolq.OK(ret)
|
return coolq.OK(ret)
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
"github.com/Mrs4s/go-cqhttp/modules/api"
|
"github.com/Mrs4s/go-cqhttp/modules/api"
|
||||||
"github.com/Mrs4s/go-cqhttp/modules/config"
|
"github.com/Mrs4s/go-cqhttp/modules/config"
|
||||||
"github.com/Mrs4s/go-cqhttp/modules/filter"
|
"github.com/Mrs4s/go-cqhttp/modules/filter"
|
||||||
|
"github.com/Mrs4s/go-cqhttp/pkg/onebot"
|
||||||
)
|
)
|
||||||
|
|
||||||
type webSocketServer struct {
|
type webSocketServer struct {
|
||||||
@ -191,9 +192,13 @@ func runWSClient(b *coolq.CQBot, node yaml.Node) {
|
|||||||
filter: conf.Filter,
|
filter: conf.Filter,
|
||||||
}
|
}
|
||||||
filter.Add(c.filter)
|
filter.Add(c.filter)
|
||||||
|
|
||||||
if conf.ReconnectInterval != 0 {
|
if conf.ReconnectInterval != 0 {
|
||||||
c.reconnectInterval = time.Duration(conf.ReconnectInterval) * time.Millisecond
|
c.reconnectInterval = time.Duration(conf.ReconnectInterval) * time.Millisecond
|
||||||
|
} else {
|
||||||
|
c.reconnectInterval = time.Second * 5
|
||||||
}
|
}
|
||||||
|
|
||||||
if conf.RateLimit.Enabled {
|
if conf.RateLimit.Enabled {
|
||||||
c.limiter = rateLimit(conf.RateLimit.Frequency, conf.RateLimit.Bucket)
|
c.limiter = rateLimit(conf.RateLimit.Frequency, conf.RateLimit.Bucket)
|
||||||
}
|
}
|
||||||
@ -463,14 +468,16 @@ func (s *webSocketServer) listenAPI(c *wsConn) {
|
|||||||
func (c *wsConn) handleRequest(_ *coolq.CQBot, payload []byte) {
|
func (c *wsConn) handleRequest(_ *coolq.CQBot, payload []byte) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := recover(); err != nil {
|
if err := recover(); err != nil {
|
||||||
log.Printf("处置WS命令时发生无法恢复的异常:%v\n%s", err, debug.Stack())
|
log.Errorf("处置WS命令时发生无法恢复的异常:%v\n%s", err, debug.Stack())
|
||||||
_ = c.Close()
|
_ = c.Close()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
j := gjson.Parse(utils.B2S(payload))
|
j := gjson.Parse(utils.B2S(payload))
|
||||||
t := strings.TrimSuffix(j.Get("action").Str, "_async")
|
t := strings.TrimSuffix(j.Get("action").Str, "_async")
|
||||||
log.Debugf("WS接收到API调用: %v 参数: %v", t, j.Get("params").Raw)
|
params := j.Get("params")
|
||||||
ret := c.apiCaller.Call(t, j.Get("params"))
|
log.Debugf("WS接收到API调用: %v 参数: %v", t, params.Raw)
|
||||||
|
ret := c.apiCaller.Call(t, onebot.V11, params)
|
||||||
if j.Get("echo").Exists() {
|
if j.Get("echo").Exists() {
|
||||||
ret["echo"] = j.Get("echo").Value()
|
ret["echo"] = j.Get("echo").Value()
|
||||||
}
|
}
|
||||||
@ -478,7 +485,11 @@ func (c *wsConn) handleRequest(_ *coolq.CQBot, payload []byte) {
|
|||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
_ = c.conn.SetWriteDeadline(time.Now().Add(time.Second * 15))
|
_ = c.conn.SetWriteDeadline(time.Now().Add(time.Second * 15))
|
||||||
writer, _ := c.conn.NextWriter(websocket.TextMessage)
|
writer, err := c.conn.NextWriter(websocket.TextMessage)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("无法响应API调用(连接已断开?): %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
_ = json.NewEncoder(writer).Encode(ret)
|
_ = json.NewEncoder(writer).Encode(ret)
|
||||||
_ = writer.Close()
|
_ = writer.Close()
|
||||||
}
|
}
|
||||||
|
1
winres/.gitignore
vendored
Normal file
1
winres/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
winres.json
|
118
winres/gen/json.go
Normal file
118
winres/gen/json.go
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
// Package main generates winres.json
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Mrs4s/go-cqhttp/internal/base"
|
||||||
|
)
|
||||||
|
|
||||||
|
const js = `{
|
||||||
|
"RT_GROUP_ICON": {
|
||||||
|
"APP": {
|
||||||
|
"0000": [
|
||||||
|
"icon.png",
|
||||||
|
"icon16.png"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"RT_MANIFEST": {
|
||||||
|
"#1": {
|
||||||
|
"0409": {
|
||||||
|
"identity": {
|
||||||
|
"name": "go-cqhttp",
|
||||||
|
"version": "%s"
|
||||||
|
},
|
||||||
|
"description": "",
|
||||||
|
"minimum-os": "vista",
|
||||||
|
"execution-level": "as invoker",
|
||||||
|
"ui-access": false,
|
||||||
|
"auto-elevate": false,
|
||||||
|
"dpi-awareness": "system",
|
||||||
|
"disable-theming": false,
|
||||||
|
"disable-window-filtering": false,
|
||||||
|
"high-resolution-scrolling-aware": false,
|
||||||
|
"ultra-high-resolution-scrolling-aware": false,
|
||||||
|
"long-path-aware": false,
|
||||||
|
"printer-driver-isolation": false,
|
||||||
|
"gdi-scaling": false,
|
||||||
|
"segment-heap": false,
|
||||||
|
"use-common-controls-v6": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"RT_VERSION": {
|
||||||
|
"#1": {
|
||||||
|
"0000": {
|
||||||
|
"fixed": {
|
||||||
|
"file_version": "%s",
|
||||||
|
"product_version": "%s",
|
||||||
|
"timestamp": "%s"
|
||||||
|
},
|
||||||
|
"info": {
|
||||||
|
"0409": {
|
||||||
|
"Comments": "Golang implementation of cqhttp.",
|
||||||
|
"CompanyName": "Mrs4s",
|
||||||
|
"FileDescription": "https://github.com/Mrs4s/go-cqhttp",
|
||||||
|
"FileVersion": "%s",
|
||||||
|
"InternalName": "",
|
||||||
|
"LegalCopyright": "©️ 2020 - %d Mrs4s. All Rights Reserved.",
|
||||||
|
"LegalTrademarks": "",
|
||||||
|
"OriginalFilename": "GOCQHTTP.EXE",
|
||||||
|
"PrivateBuild": "",
|
||||||
|
"ProductName": "go-cqhttp",
|
||||||
|
"ProductVersion": "%s",
|
||||||
|
"SpecialBuild": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
const timeformat = `2006-01-02T15:04:05+08:00`
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
f, err := os.Create("winres.json")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
v := ""
|
||||||
|
if base.Version == "(devel)" {
|
||||||
|
vartag := bytes.NewBuffer(nil)
|
||||||
|
vartagcmd := exec.Command("git", "tag", "--sort=committerdate")
|
||||||
|
vartagcmd.Stdout = vartag
|
||||||
|
err = vartagcmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
s := strings.Split(vartag.String(), "\n")
|
||||||
|
v = s[len(s)-2]
|
||||||
|
} else {
|
||||||
|
v = base.Version
|
||||||
|
}
|
||||||
|
i := strings.Index(v, "-") // remove -rc / -beta
|
||||||
|
if i <= 0 {
|
||||||
|
i = len(v)
|
||||||
|
}
|
||||||
|
commitcnt := strings.Builder{}
|
||||||
|
commitcnt.WriteString(v[1:i])
|
||||||
|
commitcnt.WriteByte('.')
|
||||||
|
commitcntcmd := exec.Command("git", "rev-list", "--count", "master")
|
||||||
|
commitcntcmd.Stdout = &commitcnt
|
||||||
|
err = commitcntcmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fv := commitcnt.String()[:commitcnt.Len()-1]
|
||||||
|
_, err = fmt.Fprintf(f, js, fv, fv, v, time.Now().Format(timeformat), fv, time.Now().Year(), v)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
BIN
winres/icon.png
Normal file
BIN
winres/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 82 KiB |
BIN
winres/icon16.png
Normal file
BIN
winres/icon16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
4
winres/init.go
Normal file
4
winres/init.go
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
// Package winres 生成windows资源
|
||||||
|
package winres
|
||||||
|
|
||||||
|
//go:generate go run github.com/Mrs4s/go-cqhttp/winres/gen
|
Reference in New Issue
Block a user