mirror of
https://github.com/woodchen-ink/Oapi-Feishu.git
synced 2025-07-18 05:42:08 +08:00
回复流式可用版
This commit is contained in:
parent
394d7e1c5d
commit
6bd56eb9b7
11
.env.example
11
.env.example
@ -1,11 +0,0 @@
|
|||||||
APP_ID=
|
|
||||||
APP_SECRET=
|
|
||||||
|
|
||||||
OPENAI_API_KEY=
|
|
||||||
|
|
||||||
OPENAI_API_SERVER=
|
|
||||||
|
|
||||||
OPENAI_API_TYPE=
|
|
||||||
OPENAI_AZURE_RESOURCE=
|
|
||||||
OPENAI_AZURE_DEPLOYMENT=
|
|
||||||
OPENAI_AZURE_API_VERSION=
|
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,5 +0,0 @@
|
|||||||
tmp/
|
|
||||||
gzchatgpt
|
|
||||||
main
|
|
||||||
chatgpt-for-chatbot-feishu
|
|
||||||
.env
|
|
@ -1,22 +0,0 @@
|
|||||||
env:
|
|
||||||
- GO111MODULE=on
|
|
||||||
|
|
||||||
builds:
|
|
||||||
- env:
|
|
||||||
- CGO_ENABLED=0
|
|
||||||
goos:
|
|
||||||
- linux
|
|
||||||
- darwin
|
|
||||||
# - windows
|
|
||||||
goarch:
|
|
||||||
- amd64
|
|
||||||
- arm64
|
|
||||||
# - arm
|
|
||||||
# - "386"
|
|
||||||
goarm:
|
|
||||||
- "7"
|
|
||||||
mod_timestamp: "{{ .CommitTimestamp }}"
|
|
||||||
flags:
|
|
||||||
- -trimpath
|
|
||||||
ldflags:
|
|
||||||
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{ .CommitDate }} -X main.builtBy=go-zoox
|
|
54
Dockerfile
54
Dockerfile
@ -1,46 +1,20 @@
|
|||||||
# Builder
|
FROM golang:1.18 as golang
|
||||||
FROM --platform=$BUILDPLATFORM whatwewant/builder-go:v1.20-1 as builder
|
|
||||||
|
ENV GO111MODULE=on \
|
||||||
|
CGO_ENABLED=1 \
|
||||||
|
GOPROXY=https://goproxy.cn,direct
|
||||||
|
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
ADD /code /build
|
||||||
|
|
||||||
COPY go.mod ./
|
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags '-w -s' -o feishu_chatgpt
|
||||||
|
|
||||||
COPY go.sum ./
|
FROM alpine:latest
|
||||||
|
|
||||||
RUN go mod download
|
WORKDIR /app
|
||||||
|
|
||||||
COPY . .
|
RUN apk add --no-cache bash
|
||||||
|
COPY --from=golang /build/feishu_chatgpt /app
|
||||||
ARG TARGETARCH
|
COPY --from=golang /build/role_list.yaml /app
|
||||||
|
EXPOSE 9000
|
||||||
RUN CGO_ENABLED=0 \
|
ENTRYPOINT ["/app/feishu_chatgpt"]
|
||||||
GOOS=linux \
|
|
||||||
GOARCH=$TARGETARCH \
|
|
||||||
go build \
|
|
||||||
-trimpath \
|
|
||||||
-ldflags '-w -s -buildid=' \
|
|
||||||
-v -o chatgpt-for-chatbot-feishu
|
|
||||||
|
|
||||||
# Server
|
|
||||||
FROM whatwewant/go:v1.20-1
|
|
||||||
# FROM whatwewant/zmicro:v1
|
|
||||||
|
|
||||||
LABEL MAINTAINER="Zero<tobewhatwewant@gmail.com>"
|
|
||||||
|
|
||||||
LABEL org.opencontainers.image.source="https://github.com/go-zoox/chatgpt-for-chatbot-feishu"
|
|
||||||
|
|
||||||
ARG VERSION=latest
|
|
||||||
|
|
||||||
ENV MODE=production
|
|
||||||
|
|
||||||
COPY --from=builder /build/chatgpt-for-chatbot-feishu /bin
|
|
||||||
|
|
||||||
ENV VERSION=${VERSION}
|
|
||||||
|
|
||||||
RUN zmicro package install ngrok
|
|
||||||
|
|
||||||
# RUN zmicro package install cpolar
|
|
||||||
|
|
||||||
COPY ./entrypoint.sh /
|
|
||||||
|
|
||||||
CMD /entrypoint.sh
|
|
||||||
|
687
LICENSE
687
LICENSE
@ -1,21 +1,674 @@
|
|||||||
MIT License
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
Copyright (c) 2023 go-zoox
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Preamble
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
The GNU General Public License is a free, copyleft license for
|
||||||
copies or substantial portions of the Software.
|
software and other kinds of works.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
The licenses for most software and other practical works are designed
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
to take away your freedom to share and change the works. By contrast,
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
share and change all versions of a program--to make sure it remains free
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
GNU General Public License for most of our software; it applies also to
|
||||||
SOFTWARE.
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||||
|
2
code/.gitignore
vendored
Normal file
2
code/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/apikey_usage.json
|
||||||
|
*.pem
|
@ -1,75 +0,0 @@
|
|||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
larkcard "github.com/larksuite/oapi-sdk-go/v3/card"
|
|
||||||
"start-feishubot/initialization"
|
|
||||||
"start-feishubot/services"
|
|
||||||
"start-feishubot/services/openai"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewRoleTagCardHandler(cardMsg CardMsg,
|
|
||||||
m MessageHandler) CardHandlerFunc {
|
|
||||||
return func(ctx context.Context, cardAction *larkcard.CardAction) (interface{}, error) {
|
|
||||||
|
|
||||||
if cardMsg.Kind == RoleTagsChooseKind {
|
|
||||||
newCard, err, done := CommonProcessRoleTag(cardMsg, cardAction,
|
|
||||||
m.sessionCache)
|
|
||||||
if done {
|
|
||||||
return newCard, err
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return nil, ErrNextHandler
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRoleCardHandler(cardMsg CardMsg,
|
|
||||||
m MessageHandler) CardHandlerFunc {
|
|
||||||
return func(ctx context.Context, cardAction *larkcard.CardAction) (interface{}, error) {
|
|
||||||
|
|
||||||
if cardMsg.Kind == RoleChooseKind {
|
|
||||||
newCard, err, done := CommonProcessRole(cardMsg, cardAction,
|
|
||||||
m.sessionCache)
|
|
||||||
if done {
|
|
||||||
return newCard, err
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return nil, ErrNextHandler
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func CommonProcessRoleTag(msg CardMsg, cardAction *larkcard.CardAction,
|
|
||||||
cache services.SessionServiceCacheInterface) (interface{},
|
|
||||||
error, bool) {
|
|
||||||
option := cardAction.Action.Option
|
|
||||||
//replyMsg(context.Background(), "已选择tag:"+option,
|
|
||||||
// &msg.MsgId)
|
|
||||||
roles := initialization.GetTitleListByTag(option)
|
|
||||||
//fmt.Printf("roles: %s", roles)
|
|
||||||
SendRoleListCard(context.Background(), &msg.SessionId,
|
|
||||||
&msg.MsgId, option, *roles)
|
|
||||||
return nil, nil, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func CommonProcessRole(msg CardMsg, cardAction *larkcard.CardAction,
|
|
||||||
cache services.SessionServiceCacheInterface) (interface{},
|
|
||||||
error, bool) {
|
|
||||||
option := cardAction.Action.Option
|
|
||||||
contentByTitle, error := initialization.GetFirstRoleContentByTitle(option)
|
|
||||||
if error != nil {
|
|
||||||
return nil, error, true
|
|
||||||
}
|
|
||||||
cache.Clear(msg.SessionId)
|
|
||||||
systemMsg := append([]openai.Messages{}, openai.Messages{
|
|
||||||
Role: "system", Content: contentByTitle,
|
|
||||||
})
|
|
||||||
cache.SetMsg(msg.SessionId, systemMsg)
|
|
||||||
//pp.Println("systemMsg: ", systemMsg)
|
|
||||||
sendSystemInstructionCard(context.Background(), &msg.SessionId,
|
|
||||||
&msg.MsgId, contentByTitle)
|
|
||||||
//replyMsg(context.Background(), "已选择角色:"+contentByTitle,
|
|
||||||
// &msg.MsgId)
|
|
||||||
return nil, nil, true
|
|
||||||
}
|
|
@ -3,10 +3,9 @@ package handlers
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
larkim "github.com/larksuite/oapi-sdk-go/v3/service/im/v1"
|
|
||||||
"start-feishubot/initialization"
|
|
||||||
"start-feishubot/services/openai"
|
|
||||||
"start-feishubot/utils"
|
"start-feishubot/utils"
|
||||||
|
|
||||||
|
larkim "github.com/larksuite/oapi-sdk-go/v3/service/im/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MsgInfo struct {
|
type MsgInfo struct {
|
||||||
@ -86,24 +85,6 @@ func (*ClearAction) Execute(a *ActionInfo) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
type RolePlayAction struct { /*角色扮演*/
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*RolePlayAction) Execute(a *ActionInfo) bool {
|
|
||||||
if system, foundSystem := utils.EitherCutPrefix(a.info.qParsed,
|
|
||||||
"/system ", "角色扮演 "); foundSystem {
|
|
||||||
a.handler.sessionCache.Clear(*a.info.sessionId)
|
|
||||||
systemMsg := append([]openai.Messages{}, openai.Messages{
|
|
||||||
Role: "system", Content: system,
|
|
||||||
})
|
|
||||||
a.handler.sessionCache.SetMsg(*a.info.sessionId, systemMsg)
|
|
||||||
sendSystemInstructionCard(*a.ctx, a.info.sessionId,
|
|
||||||
a.info.msgId, system)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
type HelpAction struct { /*帮助*/
|
type HelpAction struct { /*帮助*/
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,23 +113,3 @@ func (*BalanceAction) Execute(a *ActionInfo) bool {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
type RoleListAction struct { /*角色列表*/
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*RoleListAction) Execute(a *ActionInfo) bool {
|
|
||||||
if _, foundSystem := utils.EitherTrimEqual(a.info.qParsed,
|
|
||||||
"/roles", "角色列表"); foundSystem {
|
|
||||||
//a.handler.sessionCache.Clear(*a.info.sessionId)
|
|
||||||
//systemMsg := append([]openai.Messages{}, openai.Messages{
|
|
||||||
// Role: "system", Content: system,
|
|
||||||
//})
|
|
||||||
//a.handler.sessionCache.SetMsg(*a.info.sessionId, systemMsg)
|
|
||||||
//sendSystemInstructionCard(*a.ctx, a.info.sessionId,
|
|
||||||
// a.info.msgId, system)
|
|
||||||
tags := initialization.GetAllUniqueTags()
|
|
||||||
SendRoleTagsCard(*a.ctx, a.info.sessionId, a.info.msgId, *tags)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
@ -95,9 +95,7 @@ func (m MessageHandler) msgReceivedHandler(ctx context.Context, event *larkim.P2
|
|||||||
&ProcessMentionAction{}, //判断机器人是否应该被调用
|
&ProcessMentionAction{}, //判断机器人是否应该被调用
|
||||||
&EmptyAction{}, //空消息处理
|
&EmptyAction{}, //空消息处理
|
||||||
&ClearAction{}, //清除消息处理
|
&ClearAction{}, //清除消息处理
|
||||||
&RoleListAction{}, //角色列表处理
|
|
||||||
&HelpAction{}, //帮助处理
|
&HelpAction{}, //帮助处理
|
||||||
&RolePlayAction{}, //角色扮演处理
|
|
||||||
&MessageAction{
|
&MessageAction{
|
||||||
chatgpt: chatgpt.NewGpt3(&m.config),
|
chatgpt: chatgpt.NewGpt3(&m.config),
|
||||||
}, //消息处理
|
}, //消息处理
|
||||||
|
@ -19,13 +19,11 @@ type CardKind string
|
|||||||
type CardChatType string
|
type CardChatType string
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ClearCardKind = CardKind("clear") // 清空上下文
|
ClearCardKind = CardKind("clear") // 清空上下文
|
||||||
PicModeChangeKind = CardKind("pic_mode_change") // 切换图片创作模式
|
PicModeChangeKind = CardKind("pic_mode_change") // 切换图片创作模式
|
||||||
PicResolutionKind = CardKind("pic_resolution") // 图片分辨率调整
|
PicResolutionKind = CardKind("pic_resolution") // 图片分辨率调整
|
||||||
PicTextMoreKind = CardKind("pic_text_more") // 重新根据文本生成图片
|
PicTextMoreKind = CardKind("pic_text_more") // 重新根据文本生成图片
|
||||||
PicVarMoreKind = CardKind("pic_var_more") // 变量图片
|
PicVarMoreKind = CardKind("pic_var_more") // 变量图片
|
||||||
RoleTagsChooseKind = CardKind("role_tags_choose") // 内置角色所属标签选择
|
|
||||||
RoleChooseKind = CardKind("role_choose") // 内置角色选择
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -401,59 +399,6 @@ func withPicResolutionBtn(sessionID *string) larkcard.
|
|||||||
Build()
|
Build()
|
||||||
return actions
|
return actions
|
||||||
}
|
}
|
||||||
func withRoleTagsBtn(sessionID *string, tags ...string) larkcard.
|
|
||||||
MessageCardElement {
|
|
||||||
var menuOptions []MenuOption
|
|
||||||
|
|
||||||
for _, tag := range tags {
|
|
||||||
menuOptions = append(menuOptions, MenuOption{
|
|
||||||
label: tag,
|
|
||||||
value: tag,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
cancelMenu := newMenu("选择角色分类",
|
|
||||||
map[string]interface{}{
|
|
||||||
"value": "0",
|
|
||||||
"kind": RoleTagsChooseKind,
|
|
||||||
"sessionId": *sessionID,
|
|
||||||
"msgId": *sessionID,
|
|
||||||
},
|
|
||||||
menuOptions...,
|
|
||||||
)
|
|
||||||
|
|
||||||
actions := larkcard.NewMessageCardAction().
|
|
||||||
Actions([]larkcard.MessageCardActionElement{cancelMenu}).
|
|
||||||
Layout(larkcard.MessageCardActionLayoutFlow.Ptr()).
|
|
||||||
Build()
|
|
||||||
return actions
|
|
||||||
}
|
|
||||||
|
|
||||||
func withRoleBtn(sessionID *string, titles ...string) larkcard.
|
|
||||||
MessageCardElement {
|
|
||||||
var menuOptions []MenuOption
|
|
||||||
|
|
||||||
for _, tag := range titles {
|
|
||||||
menuOptions = append(menuOptions, MenuOption{
|
|
||||||
label: tag,
|
|
||||||
value: tag,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
cancelMenu := newMenu("查看内置角色",
|
|
||||||
map[string]interface{}{
|
|
||||||
"value": "0",
|
|
||||||
"kind": RoleChooseKind,
|
|
||||||
"sessionId": *sessionID,
|
|
||||||
"msgId": *sessionID,
|
|
||||||
},
|
|
||||||
menuOptions...,
|
|
||||||
)
|
|
||||||
|
|
||||||
actions := larkcard.NewMessageCardAction().
|
|
||||||
Actions([]larkcard.MessageCardActionElement{cancelMenu}).
|
|
||||||
Layout(larkcard.MessageCardActionLayoutFlow.Ptr()).
|
|
||||||
Build()
|
|
||||||
return actions
|
|
||||||
}
|
|
||||||
|
|
||||||
func replyMsg(ctx context.Context, msg string, msgId *string) error {
|
func replyMsg(ctx context.Context, msg string, msgId *string) error {
|
||||||
msg, i := processMessage(msg)
|
msg, i := processMessage(msg)
|
||||||
@ -645,15 +590,6 @@ func sendClearCacheCheckCard(ctx context.Context,
|
|||||||
replyCard(ctx, msgId, newCard)
|
replyCard(ctx, msgId, newCard)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendSystemInstructionCard(ctx context.Context,
|
|
||||||
sessionId *string, msgId *string, content string) {
|
|
||||||
newCard, _ := newSendCard(
|
|
||||||
withHeader("🥷 已进入角色扮演模式", larkcard.TemplateIndigo),
|
|
||||||
withMainText(content),
|
|
||||||
withNote("请注意,这将开始一个全新的对话,您将无法利用之前话题的历史信息"))
|
|
||||||
replyCard(ctx, msgId, newCard)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendOnProcessCard(ctx context.Context,
|
func sendOnProcessCard(ctx context.Context,
|
||||||
sessionId *string, msgId *string) (*string, error) {
|
sessionId *string, msgId *string) (*string, error) {
|
||||||
newCard, _ := newSendCardWithOutHeader(
|
newCard, _ := newSendCardWithOutHeader(
|
||||||
@ -694,7 +630,7 @@ func sendHelpCard(ctx context.Context,
|
|||||||
sessionId *string, msgId *string) {
|
sessionId *string, msgId *string) {
|
||||||
newCard, _ := newSendCard(
|
newCard, _ := newSendCard(
|
||||||
withHeader("🎒需要帮助吗?", larkcard.TemplateBlue),
|
withHeader("🎒需要帮助吗?", larkcard.TemplateBlue),
|
||||||
withMainMd("**我是具备打字机效果的聊天机器人!**"),
|
withMainMd("**我是具备打字机效果的Oapi飞书聊天机器人**"),
|
||||||
withSplitLine(),
|
withSplitLine(),
|
||||||
withMdAndExtraBtn(
|
withMdAndExtraBtn(
|
||||||
"** 🆑 清除话题上下文**\n文本回复 *清除* 或 */clear*",
|
"** 🆑 清除话题上下文**\n文本回复 *清除* 或 */clear*",
|
||||||
@ -704,8 +640,6 @@ func sendHelpCard(ctx context.Context,
|
|||||||
"chatType": UserChatType,
|
"chatType": UserChatType,
|
||||||
"sessionId": *sessionId,
|
"sessionId": *sessionId,
|
||||||
}, larkcard.MessageCardButtonTypeDanger)),
|
}, larkcard.MessageCardButtonTypeDanger)),
|
||||||
withMainMd("🛖 **内置角色列表** \n"+" 文本回复 *角色列表* 或 */roles*"),
|
|
||||||
withMainMd("🥷 **角色扮演模式**\n文本回复*角色扮演* 或 */system*+空格+角色信息"),
|
|
||||||
withSplitLine(),
|
withSplitLine(),
|
||||||
withMainMd("🎒 **需要更多帮助**\n文本回复 *帮助* 或 */help*"),
|
withMainMd("🎒 **需要更多帮助**\n文本回复 *帮助* 或 */help*"),
|
||||||
)
|
)
|
||||||
@ -744,21 +678,3 @@ func sendBalanceCard(ctx context.Context, msgId *string,
|
|||||||
)
|
)
|
||||||
replyCard(ctx, msgId, newCard)
|
replyCard(ctx, msgId, newCard)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendRoleTagsCard(ctx context.Context,
|
|
||||||
sessionId *string, msgId *string, roleTags []string) {
|
|
||||||
newCard, _ := newSendCard(
|
|
||||||
withHeader("🛖 请选择角色类别", larkcard.TemplateIndigo),
|
|
||||||
withRoleTagsBtn(sessionId, roleTags...),
|
|
||||||
withNote("提醒:选择角色所属分类,以便我们为您推荐更多相关角色。"))
|
|
||||||
replyCard(ctx, msgId, newCard)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SendRoleListCard(ctx context.Context,
|
|
||||||
sessionId *string, msgId *string, roleTag string, roleList []string) {
|
|
||||||
newCard, _ := newSendCard(
|
|
||||||
withHeader("🛖 角色列表"+" - "+roleTag, larkcard.TemplateIndigo),
|
|
||||||
withRoleBtn(sessionId, roleList...),
|
|
||||||
withNote("提醒:选择内置场景,快速进入角色扮演模式。"))
|
|
||||||
replyCard(ctx, msgId, newCard)
|
|
||||||
}
|
|
||||||
|
@ -2,12 +2,13 @@ package initialization
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/spf13/pflag"
|
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -85,7 +86,7 @@ func LoadConfig(cfg string) *Config {
|
|||||||
UseHttps: getViperBoolValue("USE_HTTPS", false),
|
UseHttps: getViperBoolValue("USE_HTTPS", false),
|
||||||
CertFile: getViperStringValue("CERT_FILE", "cert.pem"),
|
CertFile: getViperStringValue("CERT_FILE", "cert.pem"),
|
||||||
KeyFile: getViperStringValue("KEY_FILE", "key.pem"),
|
KeyFile: getViperStringValue("KEY_FILE", "key.pem"),
|
||||||
OpenaiApiUrl: getViperStringValue("API_URL", "https://api.openai.com"),
|
OpenaiApiUrl: getViperStringValue("API_URL", "https://oapi.czl.net"),
|
||||||
HttpProxy: getViperStringValue("HTTP_PROXY", ""),
|
HttpProxy: getViperStringValue("HTTP_PROXY", ""),
|
||||||
AzureOn: getViperBoolValue("AZURE_ON", false),
|
AzureOn: getViperBoolValue("AZURE_ON", false),
|
||||||
AzureApiVersion: getViperStringValue("AZURE_API_VERSION", "2023-03-15-preview"),
|
AzureApiVersion: getViperStringValue("AZURE_API_VERSION", "2023-03-15-preview"),
|
||||||
|
71
code/initialization/gin.go
Normal file
71
code/initialization/gin.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package initialization
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func loadCertificate(config Config) (cert tls.Certificate, err error) {
|
||||||
|
cert, err = tls.LoadX509KeyPair(config.CertFile, config.KeyFile)
|
||||||
|
if err != nil {
|
||||||
|
return cert, fmt.Errorf("failed to load certificate: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check certificate expiry
|
||||||
|
if len(cert.Certificate) == 0 {
|
||||||
|
return cert, fmt.Errorf("no certificates found in %s", config.CertFile)
|
||||||
|
}
|
||||||
|
parsedCert, err := x509.ParseCertificate(cert.Certificate[0])
|
||||||
|
if err != nil {
|
||||||
|
return cert, fmt.Errorf("failed to parse certificate: %v", err)
|
||||||
|
}
|
||||||
|
cert.Leaf = parsedCert
|
||||||
|
certExpiry := cert.Leaf.NotAfter
|
||||||
|
if certExpiry.Before(time.Now()) {
|
||||||
|
return cert, fmt.Errorf("certificate expired on %v", certExpiry)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func startHTTPServer(config Config, r *gin.Engine) (err error) {
|
||||||
|
log.Printf("http server started: http://localhost:%d/webhook/event\n", config.HttpPort)
|
||||||
|
err = r.Run(fmt.Sprintf(":%d", config.HttpPort))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to start http server: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func startHTTPSServer(config Config, r *gin.Engine) (err error) {
|
||||||
|
cert, err := loadCertificate(config)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to load certificate: %v", err)
|
||||||
|
}
|
||||||
|
server := &http.Server{
|
||||||
|
Addr: fmt.Sprintf(":%d", config.HttpsPort),
|
||||||
|
Handler: r,
|
||||||
|
TLSConfig: &tls.Config{
|
||||||
|
Certificates: []tls.Certificate{cert},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fmt.Printf("https server started: https://localhost:%d/webhook/event\n", config.HttpsPort)
|
||||||
|
err = server.ListenAndServeTLS("", "")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to start https server: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func StartServer(config Config, r *gin.Engine) (err error) {
|
||||||
|
if config.UseHttps {
|
||||||
|
err = startHTTPSServer(config, r)
|
||||||
|
} else {
|
||||||
|
err = startHTTPServer(config, r)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
15
code/initialization/lark_client.go
Normal file
15
code/initialization/lark_client.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package initialization
|
||||||
|
|
||||||
|
import (
|
||||||
|
lark "github.com/larksuite/oapi-sdk-go/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
var larkClient *lark.Client
|
||||||
|
|
||||||
|
func LoadLarkClient(config Config) {
|
||||||
|
larkClient = lark.NewClient(config.FeishuAppId, config.FeishuAppSecret)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLarkClient() *lark.Client {
|
||||||
|
return larkClient
|
||||||
|
}
|
106
code/services/loadbalancer/loadbalancer.go
Normal file
106
code/services/loadbalancer/loadbalancer.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
package loadbalancer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type API struct {
|
||||||
|
Key string
|
||||||
|
Times uint32
|
||||||
|
Available bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoadBalancer struct {
|
||||||
|
apis []*API
|
||||||
|
mu sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLoadBalancer(keys []string) *LoadBalancer {
|
||||||
|
lb := &LoadBalancer{}
|
||||||
|
for _, key := range keys {
|
||||||
|
lb.apis = append(lb.apis, &API{Key: key})
|
||||||
|
}
|
||||||
|
//SetAvailabilityForAll true
|
||||||
|
lb.SetAvailabilityForAll(true)
|
||||||
|
return lb
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lb *LoadBalancer) GetAPI() *API {
|
||||||
|
lb.mu.RLock()
|
||||||
|
defer lb.mu.RUnlock()
|
||||||
|
|
||||||
|
var availableAPIs []*API
|
||||||
|
for _, api := range lb.apis {
|
||||||
|
if api.Available {
|
||||||
|
availableAPIs = append(availableAPIs, api)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(availableAPIs) == 0 {
|
||||||
|
//随机复活一个
|
||||||
|
fmt.Printf("No available API, revive one randomly\n")
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
index := rand.Intn(len(lb.apis))
|
||||||
|
lb.apis[index].Available = true
|
||||||
|
return lb.apis[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedAPI := availableAPIs[0]
|
||||||
|
minTimes := selectedAPI.Times
|
||||||
|
for _, api := range availableAPIs {
|
||||||
|
if api.Times < minTimes {
|
||||||
|
selectedAPI = api
|
||||||
|
minTimes = api.Times
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectedAPI.Times++
|
||||||
|
//fmt.Printf("API Availability:\n")
|
||||||
|
//for _, api := range lb.apis {
|
||||||
|
// fmt.Printf("%s: %v\n", api.Key, api.Available)
|
||||||
|
// fmt.Printf("%s: %d\n", api.Key, api.Times)
|
||||||
|
//}
|
||||||
|
|
||||||
|
return selectedAPI
|
||||||
|
}
|
||||||
|
func (lb *LoadBalancer) SetAvailability(key string, available bool) {
|
||||||
|
lb.mu.Lock()
|
||||||
|
defer lb.mu.Unlock()
|
||||||
|
|
||||||
|
for _, api := range lb.apis {
|
||||||
|
if api.Key == key {
|
||||||
|
api.Available = available
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lb *LoadBalancer) RegisterAPI(key string) {
|
||||||
|
lb.mu.Lock()
|
||||||
|
defer lb.mu.Unlock()
|
||||||
|
|
||||||
|
if lb.apis == nil {
|
||||||
|
lb.apis = make([]*API, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
lb.apis = append(lb.apis, &API{Key: key})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lb *LoadBalancer) SetAvailabilityForAll(available bool) {
|
||||||
|
lb.mu.Lock()
|
||||||
|
defer lb.mu.Unlock()
|
||||||
|
|
||||||
|
for _, api := range lb.apis {
|
||||||
|
api.Available = available
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lb *LoadBalancer) GetAPIs() []*API {
|
||||||
|
lb.mu.RLock()
|
||||||
|
defer lb.mu.RUnlock()
|
||||||
|
|
||||||
|
apis := make([]*API, len(lb.apis))
|
||||||
|
copy(apis, lb.apis)
|
||||||
|
return apis
|
||||||
|
}
|
66
code/services/openai/audio.go
Normal file
66
code/services/openai/audio.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package openai
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"mime/multipart"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AudioToTextRequestBody struct {
|
||||||
|
File string `json:"file"`
|
||||||
|
Model string `json:"model"`
|
||||||
|
ResponseFormat string `json:"response_format"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AudioToTextResponseBody struct {
|
||||||
|
Text string `json:"text"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func audioMultipartForm(request AudioToTextRequestBody, w *multipart.Writer) error {
|
||||||
|
f, err := os.Open(request.File)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("opening audio file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fw, err := w.CreateFormFile("file", f.Name())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("creating form file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = io.Copy(fw, f); err != nil {
|
||||||
|
return fmt.Errorf("reading from opened audio file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fw, err = w.CreateFormField("model")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("creating form field: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
modelName := bytes.NewReader([]byte(request.Model))
|
||||||
|
if _, err = io.Copy(fw, modelName); err != nil {
|
||||||
|
return fmt.Errorf("writing model name: %w", err)
|
||||||
|
}
|
||||||
|
w.Close()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gpt *ChatGPT) AudioToText(audio string) (string, error) {
|
||||||
|
requestBody := AudioToTextRequestBody{
|
||||||
|
File: audio,
|
||||||
|
Model: "whisper-1",
|
||||||
|
ResponseFormat: "text",
|
||||||
|
}
|
||||||
|
audioToTextResponseBody := &AudioToTextResponseBody{}
|
||||||
|
err := gpt.sendRequestWithBodyType(gpt.ApiUrl+"/v1/audio/transcriptions",
|
||||||
|
"POST", formVoiceDataBody, requestBody, audioToTextResponseBody)
|
||||||
|
//fmt.Println(audioToTextResponseBody)
|
||||||
|
if err != nil {
|
||||||
|
//fmt.Println(err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return audioToTextResponseBody.Text, nil
|
||||||
|
}
|
@ -5,29 +5,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Fresh AIMode = 0.1
|
maxTokens = 2000
|
||||||
Warmth AIMode = 0.4
|
temperature = 0.7
|
||||||
Balance AIMode = 0.7
|
|
||||||
Creativity AIMode = 1.0
|
|
||||||
)
|
|
||||||
|
|
||||||
var AIModeMap = map[string]AIMode{
|
|
||||||
"清新": Fresh,
|
|
||||||
"温暖": Warmth,
|
|
||||||
"平衡": Balance,
|
|
||||||
"创意": Creativity,
|
|
||||||
}
|
|
||||||
|
|
||||||
var AIModeStrs = []string{
|
|
||||||
"清新",
|
|
||||||
"温暖",
|
|
||||||
"平衡",
|
|
||||||
"创意",
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
maxTokens = 4096
|
|
||||||
engine = "gpt-4-0613"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Messages struct {
|
type Messages struct {
|
||||||
|
297
code/services/openai/picture.go
Normal file
297
code/services/openai/picture.go
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
package openai
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/jpeg"
|
||||||
|
"image/png"
|
||||||
|
"io"
|
||||||
|
"mime/multipart"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ImageGenerationRequestBody struct {
|
||||||
|
Prompt string `json:"prompt"`
|
||||||
|
N int `json:"n"`
|
||||||
|
Size string `json:"size"`
|
||||||
|
ResponseFormat string `json:"response_format"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageResponseBody struct {
|
||||||
|
Created int64 `json:"created"`
|
||||||
|
Data []struct {
|
||||||
|
Base64Json string `json:"b64_json"`
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageVariantRequestBody struct {
|
||||||
|
Image string `json:"image"`
|
||||||
|
N int `json:"n"`
|
||||||
|
Size string `json:"size"`
|
||||||
|
ResponseFormat string `json:"response_format"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gpt *ChatGPT) GenerateImage(prompt string, size string,
|
||||||
|
n int) ([]string, error) {
|
||||||
|
requestBody := ImageGenerationRequestBody{
|
||||||
|
Prompt: prompt,
|
||||||
|
N: n,
|
||||||
|
Size: size,
|
||||||
|
ResponseFormat: "b64_json",
|
||||||
|
}
|
||||||
|
|
||||||
|
imageResponseBody := &ImageResponseBody{}
|
||||||
|
err := gpt.sendRequestWithBodyType(gpt.ApiUrl+"/v1/images/generations",
|
||||||
|
"POST", jsonBody, requestBody, imageResponseBody)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var b64Pool []string
|
||||||
|
for _, data := range imageResponseBody.Data {
|
||||||
|
b64Pool = append(b64Pool, data.Base64Json)
|
||||||
|
}
|
||||||
|
return b64Pool, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gpt *ChatGPT) GenerateOneImage(prompt string,
|
||||||
|
size string) (string, error) {
|
||||||
|
b64s, err := gpt.GenerateImage(prompt, size, 1)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return b64s[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gpt *ChatGPT) GenerateOneImageWithDefaultSize(
|
||||||
|
prompt string) (string, error) {
|
||||||
|
return gpt.GenerateOneImage(prompt, "512x512")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gpt *ChatGPT) GenerateImageVariation(images string,
|
||||||
|
size string, n int) ([]string, error) {
|
||||||
|
requestBody := ImageVariantRequestBody{
|
||||||
|
Image: images,
|
||||||
|
N: n,
|
||||||
|
Size: size,
|
||||||
|
ResponseFormat: "b64_json",
|
||||||
|
}
|
||||||
|
|
||||||
|
imageResponseBody := &ImageResponseBody{}
|
||||||
|
err := gpt.sendRequestWithBodyType(gpt.ApiUrl+"/v1/images/variations",
|
||||||
|
"POST", formPictureDataBody, requestBody, imageResponseBody)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var b64Pool []string
|
||||||
|
for _, data := range imageResponseBody.Data {
|
||||||
|
b64Pool = append(b64Pool, data.Base64Json)
|
||||||
|
}
|
||||||
|
return b64Pool, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gpt *ChatGPT) GenerateOneImageVariation(images string,
|
||||||
|
size string) (string, error) {
|
||||||
|
b64s, err := gpt.GenerateImageVariation(images, size, 1)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return b64s[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func pictureMultipartForm(request ImageVariantRequestBody,
|
||||||
|
w *multipart.Writer) error {
|
||||||
|
|
||||||
|
f, err := os.Open(request.Image)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("opening audio file: %w", err)
|
||||||
|
}
|
||||||
|
fw, err := w.CreateFormFile("image", f.Name())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("creating form file: %w", err)
|
||||||
|
}
|
||||||
|
if _, err = io.Copy(fw, f); err != nil {
|
||||||
|
return fmt.Errorf("reading from opened audio file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = w.WriteField("size", request.Size)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("writing size: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = w.WriteField("n", fmt.Sprintf("%d", request.N))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("writing n: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = w.WriteField("response_format", request.ResponseFormat)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("writing response_format: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//err = w.WriteField("user", "user123456")
|
||||||
|
|
||||||
|
//fw, err = w.CreateFormField("model")
|
||||||
|
//if err != nil {
|
||||||
|
// return fmt.Errorf("creating form field: %w", err)
|
||||||
|
//}
|
||||||
|
//modelName := bytes.NewReader([]byte(request.Model))
|
||||||
|
//if _, err = io.Copy(fw, modelName); err != nil {
|
||||||
|
// return fmt.Errorf("writing model name: %w", err)
|
||||||
|
//}
|
||||||
|
|
||||||
|
//fmt.Printf("w.FormDataContentType(): %s ", w.FormDataContentType())
|
||||||
|
|
||||||
|
w.Close()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func VerifyPngs(pngPaths []string) error {
|
||||||
|
foundPng := false
|
||||||
|
var expectedWidth, expectedHeight int
|
||||||
|
|
||||||
|
for _, pngPath := range pngPaths {
|
||||||
|
f, err := os.Open(pngPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("os.Open: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("f.Stat: %v", err)
|
||||||
|
}
|
||||||
|
if fi.Size() > 4*1024*1024 {
|
||||||
|
return fmt.Errorf("image size too large, "+
|
||||||
|
"must be under %d MB", 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
image, err := png.Decode(f)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("image must be valid png, got error: %v", err)
|
||||||
|
}
|
||||||
|
width := image.Bounds().Dx()
|
||||||
|
height := image.Bounds().Dy()
|
||||||
|
if width != height {
|
||||||
|
return fmt.Errorf("found non-square image with dimensions %dx%d", width, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !foundPng {
|
||||||
|
foundPng = true
|
||||||
|
expectedWidth = width
|
||||||
|
expectedHeight = height
|
||||||
|
} else {
|
||||||
|
if width != expectedWidth || height != expectedHeight {
|
||||||
|
return fmt.Errorf("dimensions of all images must match, got both (%dx%d) and (%dx%d)", width, height, expectedWidth, expectedHeight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConvertToRGBA(inputFilePath string, outputFilePath string) error {
|
||||||
|
// 打开输入文件
|
||||||
|
inputFile, err := os.Open(inputFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("打开文件时出错:%w", err)
|
||||||
|
}
|
||||||
|
defer inputFile.Close()
|
||||||
|
|
||||||
|
// 解码图像
|
||||||
|
img, _, err := image.Decode(inputFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("解码图像时出错:%w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将图像转换为RGBA模式
|
||||||
|
rgba := image.NewRGBA(img.Bounds())
|
||||||
|
for x := 0; x < img.Bounds().Max.X; x++ {
|
||||||
|
for y := 0; y < img.Bounds().Max.Y; y++ {
|
||||||
|
rgba.Set(x, y, img.At(x, y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建输出文件
|
||||||
|
outputFile, err := os.Create(outputFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("创建输出文件时出错:%w", err)
|
||||||
|
}
|
||||||
|
defer outputFile.Close()
|
||||||
|
|
||||||
|
// 编码图像为 PNG 格式并写入输出文件
|
||||||
|
if err := png.Encode(outputFile, rgba); err != nil {
|
||||||
|
return fmt.Errorf("编码图像时出错:%w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConvertJpegToPNG(jpgPath string) error {
|
||||||
|
// Open the JPEG file for reading
|
||||||
|
f, err := os.Open(jpgPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
// Check if the file is a JPEG image
|
||||||
|
_, err = jpeg.Decode(f)
|
||||||
|
if err != nil {
|
||||||
|
// The file is not a JPEG image, no need to convert it
|
||||||
|
return fmt.Errorf("file %s is not a JPEG image", jpgPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the file pointer to the beginning of the file
|
||||||
|
_, err = f.Seek(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new PNG file for writing
|
||||||
|
pngPath := jpgPath[:len(jpgPath)-4] + ".png" // replace .jpg extension with .png
|
||||||
|
out, err := os.Create(pngPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
// Decode the JPEG image and encode it as PNG
|
||||||
|
img, err := jpeg.Decode(f)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = png.Encode(out, img)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetImageCompressionType(path string) (string, error) {
|
||||||
|
// 打开文件
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
// 创建 bufio.Reader
|
||||||
|
reader := bufio.NewReader(file)
|
||||||
|
|
||||||
|
// 解码图像
|
||||||
|
_, format, err := image.DecodeConfig(reader)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("err: ", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("format: ", format)
|
||||||
|
// 返回压缩类型
|
||||||
|
return format, nil
|
||||||
|
}
|
BIN
code/services/openai/test_file/img.png
Normal file
BIN
code/services/openai/test_file/img.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
BIN
code/services/openai/test_file/test.jpg
Normal file
BIN
code/services/openai/test_file/test.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 83 KiB |
BIN
code/services/openai/test_file/test.wav
Normal file
BIN
code/services/openai/test_file/test.wav
Normal file
Binary file not shown.
61
code/utils/audio/ogg.go
Normal file
61
code/utils/audio/ogg.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package audio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/pion/opus"
|
||||||
|
"github.com/pion/opus/pkg/oggreader"
|
||||||
|
)
|
||||||
|
|
||||||
|
func OggToWavByPath(ogg string, wav string) error {
|
||||||
|
input, err := os.Open(ogg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer input.Close()
|
||||||
|
|
||||||
|
output, err := os.Create(wav)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer output.Close()
|
||||||
|
return OggToWav(input, output)
|
||||||
|
}
|
||||||
|
|
||||||
|
func OggToWav(input io.Reader, output io.WriteSeeker) error {
|
||||||
|
ogg, _, err := oggreader.NewWith(input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
out := make([]byte, 1920)
|
||||||
|
|
||||||
|
decoder := opus.NewDecoder()
|
||||||
|
encoder := NewEncoder(output, 44100, 16)
|
||||||
|
|
||||||
|
for {
|
||||||
|
segments, _, err := ogg.ParseNextPage()
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
break
|
||||||
|
} else if bytes.HasPrefix(segments[0], []byte("OpusTags")) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range segments {
|
||||||
|
if _, _, err = decoder.Decode(segments[i], out); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
encoder.Write(out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
encoder.Close()
|
||||||
|
return nil
|
||||||
|
}
|
107
code/utils/audio/wav.go
Normal file
107
code/utils/audio/wav.go
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
package audio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Encoder struct {
|
||||||
|
Output io.WriteSeeker
|
||||||
|
SampleRate int
|
||||||
|
BitDepth int
|
||||||
|
totalBytes uint32
|
||||||
|
isHeaderWritten bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) WriteHeader() error {
|
||||||
|
if err := writeLe(e.Output, []byte("RIFF")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writeLe(e.Output, uint32(0)); err != nil { // Placeholder for file size
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writeLe(e.Output, []byte("WAVE")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writeLe(e.Output, []byte("fmt ")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := writeLe(e.Output, uint32(16)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writeLe(e.Output, uint16(1)); err != nil { // Audio format: PCM
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := writeLe(e.Output, uint16(1)); err != nil { // Number of channels: 1 (mono)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := writeLe(e.Output, uint32(e.SampleRate)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writeLe(e.Output, uint32(e.SampleRate*e.BitDepth/8)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writeLe(e.Output, uint16(e.BitDepth/8)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := writeLe(e.Output, uint16(e.BitDepth)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writeLe(e.Output, []byte("data")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writeLe(e.Output, uint32(0)); err != nil { //Placeholder for data size
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
e.isHeaderWritten = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeLe[T []byte | uint32 | uint16 | uint8](w io.Writer, data T) error {
|
||||||
|
return binary.Write(w, binary.LittleEndian, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) Write(data []byte) error {
|
||||||
|
if !e.isHeaderWritten {
|
||||||
|
e.WriteHeader()
|
||||||
|
}
|
||||||
|
n, err := e.Output.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
e.totalBytes += uint32(n)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) Close() error {
|
||||||
|
if _, err := e.Output.Seek(4, io.SeekStart); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Write(e.Output, binary.LittleEndian, uint32(36+e.totalBytes)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := e.Output.Seek(40, io.SeekStart); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Write(e.Output, binary.LittleEndian, e.totalBytes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEncoder(w io.WriteSeeker, sampleRate int, bitDepth int) *Encoder {
|
||||||
|
return &Encoder{
|
||||||
|
SampleRate: sampleRate,
|
||||||
|
Output: w,
|
||||||
|
BitDepth: bitDepth,
|
||||||
|
isHeaderWritten: false,
|
||||||
|
}
|
||||||
|
}
|
38
code/utils/strings.go
Normal file
38
code/utils/strings.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
func CutPrefix(s, prefix string) (string, bool) {
|
||||||
|
if strings.HasPrefix(s, prefix) {
|
||||||
|
return strings.TrimPrefix(s, prefix), true
|
||||||
|
}
|
||||||
|
return s, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func EitherCutPrefix(s string, prefix ...string) (string, bool) {
|
||||||
|
// 任一前缀匹配则返回剩余部分
|
||||||
|
for _, p := range prefix {
|
||||||
|
if strings.HasPrefix(s, p) {
|
||||||
|
return strings.TrimPrefix(s, p), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// trim space and equal
|
||||||
|
func TrimEqual(s, prefix string) (string, bool) {
|
||||||
|
if strings.TrimSpace(s) == prefix {
|
||||||
|
return "", true
|
||||||
|
}
|
||||||
|
return s, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func EitherTrimEqual(s string, prefix ...string) (string, bool) {
|
||||||
|
// 任一前缀匹配则返回剩余部分
|
||||||
|
for _, p := range prefix {
|
||||||
|
if strings.TrimSpace(s) == p {
|
||||||
|
return "", true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s, false
|
||||||
|
}
|
109
code/utils/strings_test.go
Normal file
109
code/utils/strings_test.go
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestEitherCutPrefix(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
s string
|
||||||
|
prefix []string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want string
|
||||||
|
want1 bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Prefix match",
|
||||||
|
args: args{
|
||||||
|
s: "/system bar",
|
||||||
|
prefix: []string{"/system "},
|
||||||
|
},
|
||||||
|
want: "bar",
|
||||||
|
want1: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "Prefix match",
|
||||||
|
args: args{
|
||||||
|
s: "扮演 bar",
|
||||||
|
prefix: []string{"扮演 "},
|
||||||
|
},
|
||||||
|
want: "bar",
|
||||||
|
want1: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, got1 := EitherCutPrefix(tt.args.s, tt.args.prefix...)
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("EitherCutPrefix() got = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
if got1 != tt.want1 {
|
||||||
|
t.Errorf("EitherCutPrefix() got1 = %v, want %v", got1, tt.want1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEitherTrimEqual(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
s string
|
||||||
|
prefix []string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want string
|
||||||
|
want1 bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Prefix match",
|
||||||
|
args: args{
|
||||||
|
s: "清除",
|
||||||
|
prefix: []string{"清除"},
|
||||||
|
},
|
||||||
|
want: "",
|
||||||
|
want1: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Prefix match",
|
||||||
|
args: args{
|
||||||
|
s: " /clear ",
|
||||||
|
prefix: []string{"清除", "/clear"},
|
||||||
|
},
|
||||||
|
want: "",
|
||||||
|
want1: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Prefix match",
|
||||||
|
args: args{
|
||||||
|
s: " 清除 ",
|
||||||
|
prefix: []string{"清除", "/clear"},
|
||||||
|
},
|
||||||
|
want: "",
|
||||||
|
want1: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Prefix match",
|
||||||
|
args: args{
|
||||||
|
s: " clear ",
|
||||||
|
prefix: []string{"清除", "/clear"},
|
||||||
|
},
|
||||||
|
want: " clear ",
|
||||||
|
want1: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, got1 := EitherTrimEqual(tt.args.s, tt.args.prefix...)
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("EitherTrimEqual() got = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
if got1 != tt.want1 {
|
||||||
|
t.Errorf("EitherTrimEqual() got1 = %v, want %v", got1, tt.want1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -1,76 +0,0 @@
|
|||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/go-zoox/chatbot-feishu"
|
|
||||||
chatgpt "github.com/go-zoox/chatgpt-client"
|
|
||||||
"github.com/go-zoox/chatgpt-for-chatbot-feishu/config"
|
|
||||||
"github.com/go-zoox/core-utils/fmt"
|
|
||||||
"github.com/go-zoox/feishu"
|
|
||||||
feishuEvent "github.com/go-zoox/feishu/event"
|
|
||||||
"github.com/go-zoox/fetch"
|
|
||||||
"github.com/go-zoox/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CreateCustomCommand(
|
|
||||||
feishuClient feishu.Client,
|
|
||||||
chatgptClient chatgpt.Client,
|
|
||||||
cfg *config.Config,
|
|
||||||
) *chatbot.Command {
|
|
||||||
return &chatbot.Command{
|
|
||||||
ArgsLength: 1,
|
|
||||||
Handler: func(args []string, request *feishuEvent.EventRequest, reply chatbot.MessageReply) error {
|
|
||||||
if len(args) != 1 {
|
|
||||||
return fmt.Errorf("invalid args: %v", args)
|
|
||||||
}
|
|
||||||
|
|
||||||
question := args[0]
|
|
||||||
logger.Debugf("[custom command: %s, service: %s] question: %s", cfg.CustomCommand, cfg.CustomCommandService, question)
|
|
||||||
|
|
||||||
response, err := fetch.Post(cfg.CustomCommandService, &fetch.Config{
|
|
||||||
Headers: fetch.Headers{
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"Accept": "application/json",
|
|
||||||
"User-Agent": fmt.Sprintf("go-zoox_fetch/%s chatgpt-for-chatbot-feishu/%s", fetch.Version, cfg.Version),
|
|
||||||
},
|
|
||||||
Body: map[string]interface{}{
|
|
||||||
"question": args[0],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
logger.Errorf("failed to request from custom command service(%s)(1): %v", cfg.CustomCommandService, err)
|
|
||||||
if err2 := replyText(reply, fmt.Sprintf("failed to interact with command service(err: %v)", err)); err2 != nil {
|
|
||||||
return fmt.Errorf("failed to reply: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if response.Status != http.StatusOK {
|
|
||||||
logger.Errorf("failed to request from custom command service(%s)(2): %d", cfg.CustomCommandService, response.Status)
|
|
||||||
if err := replyText(reply, fmt.Sprintf("failed to interact with command service (status: %d, response: %s)", response.Status, response.String())); err != nil {
|
|
||||||
return fmt.Errorf("failed to reply: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
answer := response.Get("answer").String()
|
|
||||||
if answer == "" {
|
|
||||||
logger.Error("failed to request from custom command service(%s): empty answer (response: %s)", cfg.CustomCommandService, response.String())
|
|
||||||
if err := replyText(reply, fmt.Sprintf("no answer found, unexpected response from custom command service(response: %s)", response.String())); err != nil {
|
|
||||||
return fmt.Errorf("failed to reply: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := replyText(reply, answer); err != nil {
|
|
||||||
return fmt.Errorf("failed to reply: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/go-zoox/chatbot-feishu"
|
|
||||||
chatgpt "github.com/go-zoox/chatgpt-client"
|
|
||||||
"github.com/go-zoox/core-utils/fmt"
|
|
||||||
"github.com/go-zoox/core-utils/strings"
|
|
||||||
"github.com/go-zoox/feishu"
|
|
||||||
feishuEvent "github.com/go-zoox/feishu/event"
|
|
||||||
feishuImage "github.com/go-zoox/feishu/image"
|
|
||||||
"github.com/go-zoox/fetch"
|
|
||||||
"github.com/go-zoox/fs"
|
|
||||||
"github.com/go-zoox/logger"
|
|
||||||
openaiclient "github.com/go-zoox/openai-client"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CreateDrawCommand(
|
|
||||||
feishuClient feishu.Client,
|
|
||||||
chatgptClient chatgpt.Client,
|
|
||||||
) *chatbot.Command {
|
|
||||||
return &chatbot.Command{
|
|
||||||
Handler: func(args []string, request *feishuEvent.EventRequest, reply chatbot.MessageReply) error {
|
|
||||||
prompt := strings.Join(args, " ")
|
|
||||||
if prompt == "" {
|
|
||||||
return replyText(reply, fmt.Sprintf("prompt is required (args: %s)", strings.Join(args, " ")))
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Infof("[draw]: %v", prompt)
|
|
||||||
replyText(reply, "创作中,请稍等 ...")
|
|
||||||
|
|
||||||
logger.Infof("[draw]: request image generation ...")
|
|
||||||
response, err := chatgptClient.ImageGeneration(&openaiclient.ImageGenerationRequest{
|
|
||||||
Prompt: prompt,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return replyText(reply, fmt.Sprintf("failed to request image generation: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, image := range response.Data {
|
|
||||||
tmpFilePath := fs.TmpFilePath()
|
|
||||||
|
|
||||||
logger.Infof("[draw] download image from chatgpt: %v", image.URL)
|
|
||||||
_, err := fetch.Download(image.URL, tmpFilePath, &fetch.Config{})
|
|
||||||
if err != nil {
|
|
||||||
return replyText(reply, fmt.Sprintf("failed to download image: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpFile, err := fs.Open(tmpFilePath)
|
|
||||||
if err != nil {
|
|
||||||
return replyText(reply, fmt.Sprintf("failed to open image: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Infof("[draw] upload image to feishu ...")
|
|
||||||
response, err := feishuClient.Image().Upload(&feishuImage.UploadRequest{
|
|
||||||
ImageType: "message",
|
|
||||||
Image: tmpFile,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return replyText(reply, fmt.Sprintf("failed to upload image: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Infof("[draw] reply image to feishu: %v", response.ImageKey)
|
|
||||||
replyImage(reply, response.ImageKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,168 +0,0 @@
|
|||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/go-zoox/core-utils/regexp"
|
|
||||||
|
|
||||||
"github.com/go-zoox/chatbot-feishu"
|
|
||||||
chatgpt "github.com/go-zoox/chatgpt-client"
|
|
||||||
"github.com/go-zoox/chatgpt-for-chatbot-feishu/config"
|
|
||||||
"github.com/go-zoox/core-utils/fmt"
|
|
||||||
"github.com/go-zoox/core-utils/strings"
|
|
||||||
"github.com/go-zoox/feishu"
|
|
||||||
feishuEvent "github.com/go-zoox/feishu/event"
|
|
||||||
mc "github.com/go-zoox/feishu/message/content"
|
|
||||||
"github.com/go-zoox/logger"
|
|
||||||
"github.com/go-zoox/retry"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CreateMessageCommand(
|
|
||||||
feishuClient feishu.Client,
|
|
||||||
chatgptClient chatgpt.Client,
|
|
||||||
cfg *config.Config,
|
|
||||||
) *chatbot.Command {
|
|
||||||
return &chatbot.Command{
|
|
||||||
Handler: func(args []string, request *feishuEvent.EventRequest, reply func(content string, msgType ...string) error) (err error) {
|
|
||||||
text := strings.Join(args, " ")
|
|
||||||
|
|
||||||
// fmt.PrintJSON(request)
|
|
||||||
if cfg.BotInfo == nil {
|
|
||||||
logger.Infof("Trying to get bot info ...")
|
|
||||||
cfg.BotInfo, err = feishuClient.Bot().GetBotInfo()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to get bot info: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
user, err := getUser(feishuClient, request, cfg)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to get user: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
textMessage := strings.TrimSpace(text)
|
|
||||||
if textMessage == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var question string
|
|
||||||
// group chat
|
|
||||||
if request.IsGroupChat() {
|
|
||||||
// @
|
|
||||||
if ok := regexp.Match("^@_user_1", textMessage); ok {
|
|
||||||
for _, metion := range request.Event.Message.Mentions {
|
|
||||||
if metion.Key == "@_user_1" && metion.ID.OpenID == cfg.BotInfo.OpenID {
|
|
||||||
question = textMessage[len("@_user_1"):]
|
|
||||||
question = strings.TrimSpace(question)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if ok := regexp.Match("^/chatgpt\\s+", textMessage); ok {
|
|
||||||
// command: /chatgpt
|
|
||||||
question = textMessage[len("/chatgpt "):]
|
|
||||||
}
|
|
||||||
} else if request.IsP2pChat() {
|
|
||||||
question = textMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
question = strings.TrimSpace(question)
|
|
||||||
if question == "" {
|
|
||||||
logger.Infof("ignore empty question message")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// @TODO 离线服务
|
|
||||||
if !cfg.IsInService {
|
|
||||||
return replyText(reply, cfg.OfflineMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
logger.Debugf("%s 问 ChatGPT:%s", user.User.Name, question)
|
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
conversation, err := chatgptClient.GetOrCreateConversation(request.ChatID(), &chatgpt.ConversationConfig{
|
|
||||||
MaxMessages: 50,
|
|
||||||
Model: cfg.OpenAIModel,
|
|
||||||
Temperature: cfg.OpenAITemperature,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
logger.Errorf("failed to get or create conversation by ChatID %s", request.ChatID())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := conversation.IsQuestionAsked(request.Event.Message.MessageID); err != nil {
|
|
||||||
logger.Warnf("duplicated event(id: %s): %v", request.Event.Message.MessageID, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var answer []byte
|
|
||||||
err = retry.Retry(func() error {
|
|
||||||
|
|
||||||
answer, err = conversation.Ask([]byte(question), &chatgpt.ConversationAskConfig{
|
|
||||||
ID: request.Event.Message.MessageID,
|
|
||||||
User: user.User.Name,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
logger.Errorf("failed to request answer: %v", err)
|
|
||||||
replyText(reply, fmt.Sprintf("服务异常:%s", err.Error()))
|
|
||||||
return fmt.Errorf("failed to request answer: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}, 5, 3*time.Second)
|
|
||||||
if err != nil {
|
|
||||||
logger.Errorf("failed to get answer: %v", err)
|
|
||||||
msgType, content, err := mc.
|
|
||||||
NewContent().
|
|
||||||
Text(&mc.ContentTypeText{
|
|
||||||
Text: "ChatGPT 繁忙,请稍后重试",
|
|
||||||
}).
|
|
||||||
Build()
|
|
||||||
if err != nil {
|
|
||||||
logger.Errorf("failed to build content: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := reply(string(content), msgType); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Debugf("ChatGPT 答 %s:%s", user.User.Name, answer)
|
|
||||||
|
|
||||||
responseMessage := string(answer)
|
|
||||||
// if request.IsGroupChat() {
|
|
||||||
// responseMessage = fmt.Sprintf("%s\n-------------\n%s", question, answer)
|
|
||||||
// }
|
|
||||||
|
|
||||||
msgType, content, err := mc.
|
|
||||||
NewContent().
|
|
||||||
Post(&mc.ContentTypePost{
|
|
||||||
ZhCN: &mc.ContentTypePostBody{
|
|
||||||
Content: [][]mc.ContentTypePostBodyItem{
|
|
||||||
{
|
|
||||||
{
|
|
||||||
Tag: "text",
|
|
||||||
UnEscape: true,
|
|
||||||
Text: responseMessage,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}).
|
|
||||||
Build()
|
|
||||||
if err != nil {
|
|
||||||
logger.Errorf("failed to build content: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := reply(string(content), msgType); err != nil {
|
|
||||||
logger.Errorf("failed to reply: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/go-zoox/chatbot-feishu"
|
|
||||||
chatgpt "github.com/go-zoox/chatgpt-client"
|
|
||||||
"github.com/go-zoox/chatgpt-for-chatbot-feishu/config"
|
|
||||||
"github.com/go-zoox/core-utils/fmt"
|
|
||||||
"github.com/go-zoox/core-utils/strings"
|
|
||||||
"github.com/go-zoox/feishu"
|
|
||||||
feishuEvent "github.com/go-zoox/feishu/event"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CreateModelCommand(
|
|
||||||
feishuClient feishu.Client,
|
|
||||||
chatgptClient chatgpt.Client,
|
|
||||||
cfg *config.Config,
|
|
||||||
) *chatbot.Command {
|
|
||||||
return &chatbot.Command{
|
|
||||||
ArgsLength: 1,
|
|
||||||
Handler: func(args []string, request *feishuEvent.EventRequest, reply func(content string, msgType ...string) error) error {
|
|
||||||
if err := isAllowToDo(feishuClient, cfg, request, "model"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) == 0 || args[0] == "" {
|
|
||||||
currentModel, err := chatgptClient.GetConversationModel(request.ChatID(), &chatgpt.ConversationConfig{
|
|
||||||
MaxMessages: 100,
|
|
||||||
Model: cfg.OpenAIModel,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to get model by conversation(%s)", request.ChatID())
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := replyText(reply, fmt.Sprintf("当前模型:%s", currentModel)); err != nil {
|
|
||||||
return fmt.Errorf("failed to reply: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
model := args[0]
|
|
||||||
if model == "" {
|
|
||||||
return fmt.Errorf("model name is required (args: %s)", strings.Join(args, " "))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := chatgptClient.ChangeConversationModel(request.ChatID(), model, &chatgpt.ConversationConfig{
|
|
||||||
MaxMessages: 50,
|
|
||||||
Model: cfg.OpenAIModel,
|
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("failed to set model(%s) for conversation(%s)", model, request.ChatID())
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := replyText(reply, fmt.Sprintf("succeed to set model: %s", model)); err != nil {
|
|
||||||
return fmt.Errorf("failed to reply: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/go-zoox/chatbot-feishu"
|
|
||||||
chatgpt "github.com/go-zoox/chatgpt-client"
|
|
||||||
"github.com/go-zoox/chatgpt-for-chatbot-feishu/config"
|
|
||||||
"github.com/go-zoox/core-utils/fmt"
|
|
||||||
"github.com/go-zoox/feishu"
|
|
||||||
feishuEvent "github.com/go-zoox/feishu/event"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CreateOfflineCommand(
|
|
||||||
feishuClient feishu.Client,
|
|
||||||
chatgptClient chatgpt.Client,
|
|
||||||
cfg *config.Config,
|
|
||||||
) *chatbot.Command {
|
|
||||||
return &chatbot.Command{
|
|
||||||
Handler: func(args []string, request *feishuEvent.EventRequest, reply func(content string, msgType ...string) error) error {
|
|
||||||
if err := isAllowToDo(feishuClient, cfg, request, "online"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg.IsInService = false
|
|
||||||
|
|
||||||
if err := replyText(reply, "succeed to offline"); err != nil {
|
|
||||||
return fmt.Errorf("failed to reply: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/go-zoox/chatbot-feishu"
|
|
||||||
chatgpt "github.com/go-zoox/chatgpt-client"
|
|
||||||
"github.com/go-zoox/chatgpt-for-chatbot-feishu/config"
|
|
||||||
"github.com/go-zoox/core-utils/fmt"
|
|
||||||
"github.com/go-zoox/feishu"
|
|
||||||
feishuEvent "github.com/go-zoox/feishu/event"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CreateOnlineCommand(
|
|
||||||
feishuClient feishu.Client,
|
|
||||||
chatgptClient chatgpt.Client,
|
|
||||||
cfg *config.Config,
|
|
||||||
) *chatbot.Command {
|
|
||||||
return &chatbot.Command{
|
|
||||||
Handler: func(args []string, request *feishuEvent.EventRequest, reply func(content string, msgType ...string) error) error {
|
|
||||||
if err := isAllowToDo(feishuClient, cfg, request, "online"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg.IsInService = true
|
|
||||||
|
|
||||||
if err := replyText(reply, "succeed to online"); err != nil {
|
|
||||||
return fmt.Errorf("failed to reply: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/go-zoox/chatbot-feishu"
|
|
||||||
chatgpt "github.com/go-zoox/chatgpt-client"
|
|
||||||
"github.com/go-zoox/core-utils/fmt"
|
|
||||||
"github.com/go-zoox/feishu"
|
|
||||||
feishuEvent "github.com/go-zoox/feishu/event"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CreatePingCommand(
|
|
||||||
feishuClient feishu.Client,
|
|
||||||
chatgptClient chatgpt.Client,
|
|
||||||
) *chatbot.Command {
|
|
||||||
return &chatbot.Command{
|
|
||||||
Handler: func(args []string, request *feishuEvent.EventRequest, reply func(content string, msgType ...string) error) error {
|
|
||||||
if err := replyText(reply, "pong"); err != nil {
|
|
||||||
return fmt.Errorf("failed to reply: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/go-zoox/chatbot-feishu"
|
|
||||||
chatgpt "github.com/go-zoox/chatgpt-client"
|
|
||||||
"github.com/go-zoox/chatgpt-for-chatbot-feishu/config"
|
|
||||||
"github.com/go-zoox/core-utils/fmt"
|
|
||||||
"github.com/go-zoox/feishu"
|
|
||||||
feishuEvent "github.com/go-zoox/feishu/event"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CreateResetCommand(
|
|
||||||
feishuClient feishu.Client,
|
|
||||||
chatgptClient chatgpt.Client,
|
|
||||||
cfg *config.Config,
|
|
||||||
) *chatbot.Command {
|
|
||||||
return &chatbot.Command{
|
|
||||||
Handler: func(args []string, request *feishuEvent.EventRequest, reply chatbot.MessageReply) error {
|
|
||||||
if request.IsGroupChat() {
|
|
||||||
if err := isAllowToDo(feishuClient, cfg, request, "reset"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := chatgptClient.ResetConversation(request.ChatID()); err != nil {
|
|
||||||
return fmt.Errorf("failed to reset conversation(%s)", request.ChatID())
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := replyText(reply, "succeed to reset"); err != nil {
|
|
||||||
return fmt.Errorf("failed to reply: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,99 +0,0 @@
|
|||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/go-zoox/chatgpt-for-chatbot-feishu/config"
|
|
||||||
"github.com/go-zoox/core-utils/fmt"
|
|
||||||
"github.com/go-zoox/feishu"
|
|
||||||
"github.com/go-zoox/feishu/contact/user"
|
|
||||||
feishuEvent "github.com/go-zoox/feishu/event"
|
|
||||||
mc "github.com/go-zoox/feishu/message/content"
|
|
||||||
)
|
|
||||||
|
|
||||||
func replyText(reply func(content string, msgType ...string) error, text string) error {
|
|
||||||
msgType, content, err := mc.
|
|
||||||
NewContent().
|
|
||||||
Post(&mc.ContentTypePost{
|
|
||||||
ZhCN: &mc.ContentTypePostBody{
|
|
||||||
Content: [][]mc.ContentTypePostBodyItem{
|
|
||||||
{
|
|
||||||
{
|
|
||||||
Tag: "text",
|
|
||||||
UnEscape: true,
|
|
||||||
Text: text,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}).
|
|
||||||
Build()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to build content: %v", err)
|
|
||||||
}
|
|
||||||
if err := reply(string(content), msgType); err != nil {
|
|
||||||
return fmt.Errorf("failed to reply: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func replyImage(reply func(content string, msgType ...string) error, imageKey string) error {
|
|
||||||
msgType, content, err := mc.
|
|
||||||
NewContent().
|
|
||||||
Image(&mc.ContentTypeImage{
|
|
||||||
ImageKey: imageKey,
|
|
||||||
}).
|
|
||||||
Build()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to build content: %v", err)
|
|
||||||
}
|
|
||||||
if err := reply(string(content), msgType); err != nil {
|
|
||||||
return fmt.Errorf("failed to reply: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isAllowToDo(feishuClient feishu.Client, cfg *config.Config, request *feishuEvent.EventRequest, command string) (reason error) {
|
|
||||||
if cfg.AdminEmail != "" {
|
|
||||||
eventSender, err := feishuClient.Contact().User().Retrieve(&user.RetrieveRequest{
|
|
||||||
UserIDType: "open_id",
|
|
||||||
UserID: request.Sender().SenderID.OpenID,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to retrieve user with openid(%s): %v", request.Sender().SenderID.OpenID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if eventSender.User.EnterpriseEmail != cfg.AdminEmail && eventSender.User.Email != cfg.AdminEmail {
|
|
||||||
return fmt.Errorf("user(%s) is not allow to do action: %s", eventSender.User.Name, command)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("admin email is not set, not allow to do action: %s", command)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getUser(feishuClient feishu.Client, request *feishuEvent.EventRequest, cfg *config.Config) (*user.RetrieveResponse, error) {
|
|
||||||
sender := request.Sender()
|
|
||||||
|
|
||||||
if cfg.AdminEmail != "" {
|
|
||||||
eventSender, err := feishuClient.Contact().User().Retrieve(&user.RetrieveRequest{
|
|
||||||
UserIDType: "open_id",
|
|
||||||
UserID: sender.SenderID.OpenID,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to retrieve user with openid(%s): %v", sender.SenderID.OpenID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return eventSender, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &user.RetrieveResponse{
|
|
||||||
User: user.UserEntity{
|
|
||||||
Name: sender.SenderID.UserID,
|
|
||||||
OpenID: sender.SenderID.OpenID,
|
|
||||||
UnionID: sender.SenderID.UnionID,
|
|
||||||
UserID: sender.SenderID.UserID,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
feishuBot "github.com/go-zoox/feishu/bot"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
Port int64
|
|
||||||
APIPath string
|
|
||||||
OpenAIAPIKey string
|
|
||||||
OpenAIAPITimeout int64
|
|
||||||
AppID string
|
|
||||||
AppSecret string
|
|
||||||
EncryptKey string
|
|
||||||
VerificationToken string
|
|
||||||
//
|
|
||||||
ReportURL string
|
|
||||||
//
|
|
||||||
SiteURL string
|
|
||||||
//
|
|
||||||
OpenAIModel string
|
|
||||||
OpenAITemperature float64
|
|
||||||
//
|
|
||||||
FeishuBaseURI string
|
|
||||||
//
|
|
||||||
ConversationContext string
|
|
||||||
ConversationLanguage string
|
|
||||||
//
|
|
||||||
LogsDir string
|
|
||||||
LogsLevel string
|
|
||||||
//
|
|
||||||
OfflineMessage string
|
|
||||||
//
|
|
||||||
AdminEmail string
|
|
||||||
//
|
|
||||||
BotName string
|
|
||||||
|
|
||||||
// Proxy sets the request proxy.
|
|
||||||
// support http, https, socks5
|
|
||||||
// example:
|
|
||||||
// http://127.0.0.1:17890
|
|
||||||
// https://127.0.0.1:17890
|
|
||||||
// socks5://127.0.0.1:17890
|
|
||||||
Proxy string
|
|
||||||
|
|
||||||
OpenAIAPIServer string
|
|
||||||
|
|
||||||
OpenAIAPIType string
|
|
||||||
OpenAIAzureResource string
|
|
||||||
OpenAIAzureDeployment string
|
|
||||||
OpenAIAzureAPIVersion string
|
|
||||||
|
|
||||||
// Custom Command with Service
|
|
||||||
CustomCommand string
|
|
||||||
CustomCommandService string
|
|
||||||
|
|
||||||
//
|
|
||||||
Version string
|
|
||||||
|
|
||||||
// @TODO State
|
|
||||||
IsInService bool
|
|
||||||
BotInfo *feishuBot.GetBotInfoResponse
|
|
||||||
}
|
|
@ -1,12 +1,9 @@
|
|||||||
version: '3.3'
|
version: '3.3'
|
||||||
services:
|
services:
|
||||||
feishu-chatgpt:
|
image: woodchen/oapi-feishu
|
||||||
container_name: Feishu-OpenAI-Stream-Chatbot
|
oapi-feishu:
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
ports:
|
ports:
|
||||||
- "9000:9000/tcp"
|
- "1008:9000"
|
||||||
volumes:
|
volumes:
|
||||||
# - ./code/config.yaml:/app/config.yaml:ro
|
# - ./code/config.yaml:/app/config.yaml:ro
|
||||||
# 要注意,这里右边的容器内的路径,不是从根目录开始的,要参考 dockerfile 中的 WORKDIR
|
# 要注意,这里右边的容器内的路径,不是从根目录开始的,要参考 dockerfile 中的 WORKDIR
|
||||||
@ -29,14 +26,12 @@ services:
|
|||||||
- USE_HTTPS=false
|
- USE_HTTPS=false
|
||||||
- CERT_FILE=cert.pem
|
- CERT_FILE=cert.pem
|
||||||
- KEY_FILE=key.pem
|
- KEY_FILE=key.pem
|
||||||
# OpenAI 地址, 一般不需要修改, 除非你有自己的反向代理
|
# OpenAI 地址, 一般不需要修改
|
||||||
- API_URL=https://api.openai.com
|
- API_URL=https://oapi.czl.net
|
||||||
# 代理设置, 例如 - HTTP_PROXY=http://127.0.0.1:7890, 默认代表不使用代理
|
|
||||||
- HTTP_PROXY
|
|
||||||
## 访问控制
|
## 访问控制
|
||||||
# 是否启用访问控制。默认不启用。
|
# 是否启用访问控制。默认不启用。
|
||||||
- ACCESS_CONTROL_ENABLE=false
|
- ACCESS_CONTROL_ENABLE=false
|
||||||
# 每个用户每天最多问多少个问题。默认为0. 配置成为小于等于0表示不限制。
|
# 每个用户每天最多问多少个问题。默认为0. 配置成为小于等于0表示不限制。
|
||||||
- ACCESS_CONTROL_MAX_COUNT_PER_USER_PER_DAY=0
|
- ACCESS_CONTROL_MAX_COUNT_PER_USER_PER_DAY=0
|
||||||
# 访问OpenAi的 普通 Http请求的超时时间,单位秒,不配置的话默认为 550 秒
|
# 访问OpenAi的 普通 Http请求的超时时间,单位秒,不配置的话默认为 550 秒
|
||||||
- OPENAI_HTTP_CLIENT_TIMEOUT
|
- OPENAI_HTTP_CLIENT_TIMEOUT=600
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
version: '3.3'
|
|
||||||
services:
|
|
||||||
oapi-feishu:
|
|
||||||
image: woodchen/oapi-feishu
|
|
||||||
ports:
|
|
||||||
- 1008:8080
|
|
||||||
environment:
|
|
||||||
OPENAI_API_KEY: ${OPENAI_API_KEY}
|
|
||||||
APP_ID: ${APP_ID}
|
|
||||||
APP_SECRET: ${APP_SECRET}
|
|
||||||
CHATGPT_LANGUAGE: ZH_CN
|
|
||||||
OFFLINE_MESSAGE: GPT已离线
|
|
||||||
volumes:
|
|
||||||
- ./entrypoint.sh:/entrypoint.sh
|
|
@ -1,89 +0,0 @@
|
|||||||
#!/bin/zmicro
|
|
||||||
|
|
||||||
help() {
|
|
||||||
echo "Usage:"
|
|
||||||
echo " chatgpt-for-chatbot-feishu"
|
|
||||||
}
|
|
||||||
|
|
||||||
core() {
|
|
||||||
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
|
|
||||||
help
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
dotenv::try_load
|
|
||||||
|
|
||||||
local PORT=${PORT:-8080}
|
|
||||||
local API_PATH=${API_PATH:-"/"}
|
|
||||||
|
|
||||||
log::info "[$(timestamp)] run chatgpt for chatbot feishu with zmicro ..."
|
|
||||||
|
|
||||||
if [ "$TUNNEL_ENABLE" = "true" ]; then
|
|
||||||
runtunnel
|
|
||||||
fi
|
|
||||||
|
|
||||||
log::info "[$(timestamp)] starting chatgpt for chatbot feishu ..."
|
|
||||||
chatgpt-for-chatbot-feishu
|
|
||||||
}
|
|
||||||
|
|
||||||
runtunnel() {
|
|
||||||
local tunnel_type="$TUNNEL_TYPE"
|
|
||||||
local tunnel_auth_token="$TUNNEL_AUTH_TOKEN"
|
|
||||||
local tunnel_subdomain="$TUNNEL_SUBDOMAIN"
|
|
||||||
local tunnel_log=$(os::tmp_file)
|
|
||||||
|
|
||||||
echo
|
|
||||||
|
|
||||||
log::info "[$(timestamp)] enable tunnel $tunnel_type (logfile: $tunnel_log)..."
|
|
||||||
|
|
||||||
# TUNNEL NGROK
|
|
||||||
if [ "$tunnel_type" = "ngrok" ]; then
|
|
||||||
if [ -n "$tunnel_subdomain" ] && [ -z "$tunnel_auth_token" ]; then
|
|
||||||
log::error "[$(timestamp)] tunnel_auth_token is required when use tunnel_subdomain"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$tunnel_auth_token" ]; then
|
|
||||||
zmicro ngrok config add-authtoken $tunnel_auth_token >>/dev/null
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$tunnel_subdomain" ]; then
|
|
||||||
zmicro ngrok http --subdomain "$tunnel_subdomain" ${PORT} --log $tunnel_log >>$tunnel_log 2>&1 &
|
|
||||||
else
|
|
||||||
zmicro ngrok http ${PORT} --log $tunnel_log >>$tunnel_log 2>&1 &
|
|
||||||
fi
|
|
||||||
|
|
||||||
log::info "[$(timestamp)] starting ngrok ..."
|
|
||||||
# sleep 3
|
|
||||||
|
|
||||||
local url=""
|
|
||||||
while [ -z "$url" ]; do
|
|
||||||
sleep 1
|
|
||||||
|
|
||||||
log::info "[$(timestamp)] checking whether ngrok connected ..."
|
|
||||||
url=$(cat $tunnel_log | grep "url=" | head -n 1 | awk -F '=' '{print $8}')
|
|
||||||
if [ -n "$url" ]; then
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$DEBUG" = "true" ]; then
|
|
||||||
log::info "[$(timestamp)] show ngrok connection info start ..."
|
|
||||||
cat $tunnel_log
|
|
||||||
log::info "[$(timestamp)] show ngrok connection info end ..."
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
log::info "[$(timestamp)] ngrok url: $(color::green $url)"
|
|
||||||
|
|
||||||
export SITE_URL=$url
|
|
||||||
else
|
|
||||||
log::error "[$(timestamp)] unsupport TUNNEL_TYPE: $TUNNEL_TYPE"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
run() {
|
|
||||||
core "$@"
|
|
||||||
}
|
|
||||||
|
|
||||||
run "$@"
|
|
119
go.mod
119
go.mod
@ -1,119 +0,0 @@
|
|||||||
module github.com/go-zoox/chatgpt-for-chatbot-feishu
|
|
||||||
|
|
||||||
go 1.20
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/go-zoox/chalk v1.0.2
|
|
||||||
github.com/go-zoox/chatbot-feishu v1.3.0
|
|
||||||
github.com/go-zoox/chatgpt-client v1.6.3
|
|
||||||
github.com/go-zoox/cli v1.3.6
|
|
||||||
github.com/go-zoox/core-utils v1.3.1
|
|
||||||
github.com/go-zoox/feishu v1.4.2
|
|
||||||
github.com/go-zoox/fetch v1.8.1
|
|
||||||
github.com/go-zoox/fs v1.3.14
|
|
||||||
github.com/go-zoox/logger v1.4.6
|
|
||||||
github.com/go-zoox/openai-client v1.5.2
|
|
||||||
github.com/go-zoox/retry v1.0.3
|
|
||||||
github.com/go-zoox/zoox v1.12.37
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
|
||||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
|
||||||
github.com/creack/pty v1.1.21 // indirect
|
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
|
||||||
github.com/distribution/reference v0.5.0 // indirect
|
|
||||||
github.com/docker/cli v24.0.7+incompatible // indirect
|
|
||||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
|
||||||
github.com/docker/docker v24.0.7+incompatible // indirect
|
|
||||||
github.com/docker/go-connections v0.4.0 // indirect
|
|
||||||
github.com/docker/go-units v0.5.0 // indirect
|
|
||||||
github.com/fatih/color v1.16.0 // indirect
|
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
|
||||||
github.com/go-errors/errors v1.5.1 // indirect
|
|
||||||
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
|
||||||
github.com/go-zoox/cache v1.0.6 // indirect
|
|
||||||
github.com/go-zoox/command v1.3.0 // indirect
|
|
||||||
github.com/go-zoox/commands-as-a-service v1.6.10 // indirect
|
|
||||||
github.com/go-zoox/compress v1.0.1 // indirect
|
|
||||||
github.com/go-zoox/concurrency v1.2.0 // indirect
|
|
||||||
github.com/go-zoox/config v1.2.10 // indirect
|
|
||||||
github.com/go-zoox/cookie v1.2.0 // indirect
|
|
||||||
github.com/go-zoox/counter v1.2.1 // indirect
|
|
||||||
github.com/go-zoox/cron v1.2.0 // indirect
|
|
||||||
github.com/go-zoox/crypto v1.1.8 // indirect
|
|
||||||
github.com/go-zoox/datetime v1.2.2 // indirect
|
|
||||||
github.com/go-zoox/debug v1.0.2 // indirect
|
|
||||||
github.com/go-zoox/dotenv v1.2.3 // indirect
|
|
||||||
github.com/go-zoox/encoding v1.2.1 // indirect
|
|
||||||
github.com/go-zoox/errors v1.0.2 // indirect
|
|
||||||
github.com/go-zoox/gzip v1.0.0 // indirect
|
|
||||||
github.com/go-zoox/headers v1.0.8 // indirect
|
|
||||||
github.com/go-zoox/i18n v1.0.3 // indirect
|
|
||||||
github.com/go-zoox/ini v1.0.4 // indirect
|
|
||||||
github.com/go-zoox/jobqueue v1.0.0 // indirect
|
|
||||||
github.com/go-zoox/jsonrpc v1.2.2 // indirect
|
|
||||||
github.com/go-zoox/jwt v1.3.0 // indirect
|
|
||||||
github.com/go-zoox/kv v1.5.9 // indirect
|
|
||||||
github.com/go-zoox/lru v1.0.1 // indirect
|
|
||||||
github.com/go-zoox/mq v1.0.1 // indirect
|
|
||||||
github.com/go-zoox/proxy v1.5.6 // indirect
|
|
||||||
github.com/go-zoox/pubsub v1.2.2 // indirect
|
|
||||||
github.com/go-zoox/random v1.0.4 // indirect
|
|
||||||
github.com/go-zoox/ratelimit v1.2.1 // indirect
|
|
||||||
github.com/go-zoox/safe v1.0.1 // indirect
|
|
||||||
github.com/go-zoox/session v1.2.0 // indirect
|
|
||||||
github.com/go-zoox/tag v1.2.3 // indirect
|
|
||||||
github.com/go-zoox/uuid v0.0.1 // indirect
|
|
||||||
github.com/goccy/go-yaml v1.11.2 // indirect
|
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
|
||||||
github.com/google/uuid v1.5.0 // indirect
|
|
||||||
github.com/gorilla/websocket v1.5.1 // indirect
|
|
||||||
github.com/joho/godotenv v1.5.1 // indirect
|
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
|
||||||
github.com/moby/term v0.5.0 // indirect
|
|
||||||
github.com/morikuni/aec v1.0.0 // indirect
|
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
|
||||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
|
||||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
|
||||||
github.com/redis/go-redis/v9 v9.3.0 // indirect
|
|
||||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
|
||||||
github.com/sevlyar/go-daemon v0.1.6 // indirect
|
|
||||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
|
||||||
github.com/spf13/cast v1.6.0 // indirect
|
|
||||||
github.com/tidwall/gjson v1.17.0 // indirect
|
|
||||||
github.com/tidwall/match v1.1.1 // indirect
|
|
||||||
github.com/tidwall/pretty v1.2.1 // indirect
|
|
||||||
github.com/urfave/cli/v2 v2.25.7 // indirect
|
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
|
||||||
golang.org/x/crypto v0.16.0 // indirect
|
|
||||||
golang.org/x/mod v0.14.0 // indirect
|
|
||||||
golang.org/x/net v0.19.0 // indirect
|
|
||||||
golang.org/x/sys v0.15.0 // indirect
|
|
||||||
golang.org/x/text v0.14.0 // indirect
|
|
||||||
golang.org/x/tools v0.16.1 // indirect
|
|
||||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
|
||||||
)
|
|
||||||
|
|
||||||
// replace github.com/go-zoox/chatbot-feishu => ../chatbot-feishu
|
|
||||||
|
|
||||||
// replace github.com/go-zoox/feishu => ../feishu
|
|
||||||
|
|
||||||
// replace github.com/go-zoox/logger => ../logger
|
|
||||||
|
|
||||||
// replace github.com/go-zoox/chatbot-feishu => ../../go-zoox/chatbot-feishu
|
|
||||||
|
|
||||||
// replace github.com/go-zoox/openai-client => ../../go-zoox/openai-client
|
|
||||||
|
|
||||||
// replace github.com/go-zoox/chatgpt-client => ../../go-zoox/chatgpt-client
|
|
||||||
|
|
||||||
// replace github.com/go-zoox/feishu => ../../go-zoox/feishu
|
|
||||||
|
|
||||||
// replace github.com/go-zoox/zoox => ../zoox
|
|
444
go.sum
444
go.sum
@ -1,444 +0,0 @@
|
|||||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
|
||||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
|
||||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
|
||||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
|
||||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
|
||||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
|
||||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
|
||||||
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
|
|
||||||
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
|
||||||
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
|
|
||||||
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
|
||||||
github.com/docker/cli v24.0.7+incompatible h1:wa/nIwYFW7BVTGa7SWPVyyXU9lgORqUb1xfI36MSkFg=
|
|
||||||
github.com/docker/cli v24.0.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
|
||||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
|
||||||
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
|
||||||
github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM=
|
|
||||||
github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
|
||||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
|
||||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
|
||||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
|
||||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
|
||||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
|
||||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
|
||||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
|
||||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
|
||||||
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
|
||||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
|
||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
|
||||||
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
|
|
||||||
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
|
||||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
|
||||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
|
||||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
|
||||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
|
||||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
|
||||||
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
|
||||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
|
||||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
|
||||||
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
|
||||||
github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw=
|
|
||||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
|
||||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
|
||||||
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
|
|
||||||
github.com/go-zoox/cache v1.0.6 h1:Ozix1WuLvyMb4laZozL2LVEjYA5SwjmoFfqUXO3Luhg=
|
|
||||||
github.com/go-zoox/cache v1.0.6/go.mod h1:rDQPnldnf1V8tKCn5e1MrkDXI2BFTws7PniTQQa2lpc=
|
|
||||||
github.com/go-zoox/chalk v1.0.1/go.mod h1:z5+qvE9nEJI5uT4px2tyoFa/xxkqf3CUo22KmXLKbNI=
|
|
||||||
github.com/go-zoox/chalk v1.0.2 h1:DCWft37fogmvqF37JdbGSLg28L/tQeA8u0lMvb62KOg=
|
|
||||||
github.com/go-zoox/chalk v1.0.2/go.mod h1:z5+qvE9nEJI5uT4px2tyoFa/xxkqf3CUo22KmXLKbNI=
|
|
||||||
github.com/go-zoox/chatbot-feishu v1.3.0 h1:FW8F0ApQRQELHpwaG5dzrSLwXBTBegRs1eGILXC/UnA=
|
|
||||||
github.com/go-zoox/chatbot-feishu v1.3.0/go.mod h1:EmJRYc27aLYWoPAupEsUBmzhn8r/5YYbzgAGXZjBlHo=
|
|
||||||
github.com/go-zoox/chatgpt-client v1.6.3 h1:+xNDXQWT5wJ18fgzenWxu63XA8LcJTfANkq4X8xhom8=
|
|
||||||
github.com/go-zoox/chatgpt-client v1.6.3/go.mod h1:kVESS3pFzNvMOhuxMbT2Je+OgQfs4svP80LaO/2fV/o=
|
|
||||||
github.com/go-zoox/cli v1.3.6 h1:+A4l9aBBvqO/xXjwZ1PCcX0ue/HZ/Ft0Wp/dDq27iVE=
|
|
||||||
github.com/go-zoox/cli v1.3.6/go.mod h1:25ox3mVRJdRSyLJLvK8OHYEuXtMtfkTseWkJjlK9kM4=
|
|
||||||
github.com/go-zoox/command v1.2.9 h1:7vY0TJ3iXB7qmmhdmx+CcRWDrrPq0ITEm8NXLMLT/g4=
|
|
||||||
github.com/go-zoox/command v1.2.9/go.mod h1:U6Jt52RImxJVpSHAwYl4V5eESk5QRpRXoboXqu1oz5k=
|
|
||||||
github.com/go-zoox/command v1.3.0 h1:9VpRqEYXWJTbVjrfkTmKYmWSDqpYv7TKsQ9nker8yAE=
|
|
||||||
github.com/go-zoox/command v1.3.0/go.mod h1:U6Jt52RImxJVpSHAwYl4V5eESk5QRpRXoboXqu1oz5k=
|
|
||||||
github.com/go-zoox/commands-as-a-service v1.6.5 h1:JQJpgP9VorTySGDP1gd1jC/Ildy4gTok6zWkD3S9NEs=
|
|
||||||
github.com/go-zoox/commands-as-a-service v1.6.5/go.mod h1:oWIRqTWIzmJdgVB1UoAgiCfFoswhJ8PDfATmb+JLsHE=
|
|
||||||
github.com/go-zoox/commands-as-a-service v1.6.10 h1:bpTgqTcy0SzJX7swlct/Iw67ueiuzVXuqgW7zuDtEHE=
|
|
||||||
github.com/go-zoox/commands-as-a-service v1.6.10/go.mod h1:he7ThLjNUzWzw5sZNANoxsmUvEYSprvlQNbHw+iJagw=
|
|
||||||
github.com/go-zoox/compress v1.0.1 h1:EyNxo5NscMLua5fvUdiGSF+BwhuTfMeyppu7OwKAW7Q=
|
|
||||||
github.com/go-zoox/compress v1.0.1/go.mod h1:iV6CcNulf3OuEfA1h1VOsaBqYH81cVSg5wNi5HDx2h4=
|
|
||||||
github.com/go-zoox/concurrency v1.2.0 h1:iucwSWQ0Y9fFIG+eZvyHjMrIPSnaKJTGfOcGbIx91yg=
|
|
||||||
github.com/go-zoox/concurrency v1.2.0/go.mod h1:rghauUPHEDp8HJzaVlU851HWqiAqD8lUVp45K/dtNvw=
|
|
||||||
github.com/go-zoox/config v1.2.10 h1:mebuz6O0N81OXOCwtV+LKOiFuAfZ5wyaGsuzkGSSpf4=
|
|
||||||
github.com/go-zoox/config v1.2.10/go.mod h1:KnSEhz7AVMqQfznJzgpzFhQfgy8UZYeone/osbvnVUE=
|
|
||||||
github.com/go-zoox/cookie v1.2.0 h1:MO33lPQ/QGJIAEzgrsAfEpJc25lcJ/XR0w+smM19sNQ=
|
|
||||||
github.com/go-zoox/cookie v1.2.0/go.mod h1:+xEawxty0L+z+4EIvTF2AaHUkUM7oIecGZ9XrEaYqsI=
|
|
||||||
github.com/go-zoox/core-utils v1.0.4/go.mod h1:EknM3KLL6/kagL95wZbWE7mRRcYCG/fHqiJ/EH0ihAs=
|
|
||||||
github.com/go-zoox/core-utils v1.0.12/go.mod h1:EknM3KLL6/kagL95wZbWE7mRRcYCG/fHqiJ/EH0ihAs=
|
|
||||||
github.com/go-zoox/core-utils v1.0.13/go.mod h1:EknM3KLL6/kagL95wZbWE7mRRcYCG/fHqiJ/EH0ihAs=
|
|
||||||
github.com/go-zoox/core-utils v1.3.1 h1:uH8cWA3hYOox68+b87/j4sxUGkHjqf/EFsN/JIasCFE=
|
|
||||||
github.com/go-zoox/core-utils v1.3.1/go.mod h1:raOOwr2l2sJQyjR0Dg33sg0ry4U1/L2eNTuLFRpUXWs=
|
|
||||||
github.com/go-zoox/counter v1.0.1/go.mod h1:PICilZTrnO4dFstDPlXpjc6sdWYBG7hm/XDmjAcHaX0=
|
|
||||||
github.com/go-zoox/counter v1.2.1 h1:MPShpjJWQ/qt3pYQxuyBL/Ci6X4mtihTC3cwDr1VOI8=
|
|
||||||
github.com/go-zoox/counter v1.2.1/go.mod h1:gOA/Bk2iWt9K4vm2Std2ciEgHwQqWSzvcyUI+jFVduY=
|
|
||||||
github.com/go-zoox/cron v1.1.2 h1:4iEIXIYu8MFRWBZccPaQ7wkpmvBs/N6a0FzWYiup9AU=
|
|
||||||
github.com/go-zoox/cron v1.1.2/go.mod h1:7aOIpwGDyyg+NqX+041NO+9f8FhAngWiLqBPBN6kZ9k=
|
|
||||||
github.com/go-zoox/cron v1.2.0 h1:WAcfeg0R6sASs9HjNwDz+uEIUrl62jj0zBSOF3oox9c=
|
|
||||||
github.com/go-zoox/cron v1.2.0/go.mod h1:vN6JqnvSgF9Rjjg/+cTxm8HywA3U+0cwP9RW3kdkg4E=
|
|
||||||
github.com/go-zoox/crypto v1.0.3/go.mod h1:HaWRg4tHZamqNyOnNoaK+Rw5eto+su66i8bMMP8UCBU=
|
|
||||||
github.com/go-zoox/crypto v1.1.8 h1:oI2KPLy+SsGeb+h5A99n9MTQVp4jBhwJWkqjStUzz9I=
|
|
||||||
github.com/go-zoox/crypto v1.1.8/go.mod h1:JqgNr9HcFFGQkMCGLJ9djtfg/RWVLxtunG01HD3lUXM=
|
|
||||||
github.com/go-zoox/datetime v1.0.4/go.mod h1:os6lYW/GXNpCIseFrBr8DNcOiPuwl5Ttc/kxo4JnMjw=
|
|
||||||
github.com/go-zoox/datetime v1.2.2 h1:JrI4ekdsvpsenGzrNQAOmobBTYyotaXD3YDXngvCbM4=
|
|
||||||
github.com/go-zoox/datetime v1.2.2/go.mod h1:qvaCrzjhq/g/gstx4sx06Nl4ll2pLSrkRa9ueLjrZ9A=
|
|
||||||
github.com/go-zoox/debug v1.0.1/go.mod h1:7HvnBeV1dVuuGVnXSLdJ5OE6X/wIXAjIEyiaA7NqQPA=
|
|
||||||
github.com/go-zoox/debug v1.0.2 h1:nnaSGUC1F3218P3BN6ZhRR5GNKtx5DKZ1RtsvAhIjyA=
|
|
||||||
github.com/go-zoox/debug v1.0.2/go.mod h1:7HvnBeV1dVuuGVnXSLdJ5OE6X/wIXAjIEyiaA7NqQPA=
|
|
||||||
github.com/go-zoox/dotenv v1.0.7/go.mod h1:N2bXxghq3Zk+lpiOxv+m+0yjnGubVqJGJDR/9jWm3N0=
|
|
||||||
github.com/go-zoox/dotenv v1.1.0/go.mod h1:elKALomz436YKX4Syo+f02ozIQYq2yFaprj6jL6GnCQ=
|
|
||||||
github.com/go-zoox/dotenv v1.2.3 h1:9wx4sL2u/FrRLkzoOb7ozYii6NoGsl05KoGdZm1ebIE=
|
|
||||||
github.com/go-zoox/dotenv v1.2.3/go.mod h1:hhl5WZUxI+DNJN6Zos1y7Nq0jDCXciphswPSYtInqg0=
|
|
||||||
github.com/go-zoox/encoding v1.0.5/go.mod h1:WFF+m/IWYHtClMrcKnrdZHHf3cdIF8de2uTOILPM7lw=
|
|
||||||
github.com/go-zoox/encoding v1.0.7/go.mod h1:h6ZgI27IaUSBq1ntaZjnSruPdRFuBLNUmfp57TaPxs4=
|
|
||||||
github.com/go-zoox/encoding v1.2.1 h1:38rQRsfL1f1YHZaqsPaGcNMkPnzatnPlYiHioUh9F4A=
|
|
||||||
github.com/go-zoox/encoding v1.2.1/go.mod h1:NdcM7Ln73oVW3vJgx3MH4fJknCcdQfq+NgJ0tuCo7tU=
|
|
||||||
github.com/go-zoox/errors v1.0.2 h1:1NLMoEVlDU1+qrvvPj+rrJXOvQPdeZ3DekVBFrI5PFY=
|
|
||||||
github.com/go-zoox/errors v1.0.2/go.mod h1:HJ5NKQb9cu3IbI0Jayw7xZiblLBEIglpaIOMxvQnWnk=
|
|
||||||
github.com/go-zoox/feishu v1.4.0 h1:zdk0Qf4RuvVBUH/S/eOqgV1ZHS4IuphZkYT4TauTtBM=
|
|
||||||
github.com/go-zoox/feishu v1.4.0/go.mod h1:Wz3RsfxM9mZEMvsFR1tHbkqrjk9XvXioQo8Gnr5kvbk=
|
|
||||||
github.com/go-zoox/feishu v1.4.2 h1:XU+M4r8ga5JPO3dDdRbkc5Fd+0ShXy3c6xJd0TE6cFk=
|
|
||||||
github.com/go-zoox/feishu v1.4.2/go.mod h1:Wz3RsfxM9mZEMvsFR1tHbkqrjk9XvXioQo8Gnr5kvbk=
|
|
||||||
github.com/go-zoox/fetch v1.3.5/go.mod h1:AkS6v/DlotjmUs+7qJsoFGFkpROr9LVtiKYb000v3Kw=
|
|
||||||
github.com/go-zoox/fetch v1.4.4/go.mod h1:WExVnds3HZ1A6jJu9KuqtICfkJpvUdR4uONUnw9TG+0=
|
|
||||||
github.com/go-zoox/fetch v1.7.16 h1:3bRoF2bG+M5PnWoFXka64PfHRx35Y60Mu70z2F5USDM=
|
|
||||||
github.com/go-zoox/fetch v1.7.16/go.mod h1:zaDVj8s8gPFzEeFFsD2oWc5D1z8uKKQwwnilmPdSQOk=
|
|
||||||
github.com/go-zoox/fetch v1.8.1 h1:Zh2yW/loXSoSebKZ1UkKKMpH/E51HtFb9L0zpr+2R2I=
|
|
||||||
github.com/go-zoox/fetch v1.8.1/go.mod h1:RaYJe2EZ/4M9zUyJl9x8HhMkQ/4HfxvdqYVkRp8IY1k=
|
|
||||||
github.com/go-zoox/fs v1.2.4/go.mod h1:aywpClMqf6YO8+QnuwC3p3EvFWe88h0tWH65rpxtY00=
|
|
||||||
github.com/go-zoox/fs v1.2.5/go.mod h1:aywpClMqf6YO8+QnuwC3p3EvFWe88h0tWH65rpxtY00=
|
|
||||||
github.com/go-zoox/fs v1.3.14 h1:u5vws9DdxCKh6U6SztIGIzVcZyydXbpSrV0S+cXzP7c=
|
|
||||||
github.com/go-zoox/fs v1.3.14/go.mod h1:GGcmvYa1Kyvspc8YzPt0peLGie+KlCoo2gkg4XbGRiY=
|
|
||||||
github.com/go-zoox/gzip v1.0.0 h1:11ZTgxAPgexmZ/NJaEEuN2FDCJuvg9sips+XDR+48Yw=
|
|
||||||
github.com/go-zoox/gzip v1.0.0/go.mod h1:7g9vTpKek1dft1Yi1Ryi4A6dq9snMgq94Qq8wSte8L0=
|
|
||||||
github.com/go-zoox/headers v1.0.8 h1:HZJisMHhKwdySVNbV4Awc5kaMxFfAwBIHpcWOGch+iw=
|
|
||||||
github.com/go-zoox/headers v1.0.8/go.mod h1:WEgEbewswEw4n4qS1iG68Kn/vOQVCAKGwwuZankc6so=
|
|
||||||
github.com/go-zoox/i18n v1.0.3 h1:PqeOKyhI9MxbA9TyWDgm7zcCL5WRSlxhANHWou04VHk=
|
|
||||||
github.com/go-zoox/i18n v1.0.3/go.mod h1:WURpyaWOrVVN4f3mEQtl5A0kie5bK4ExQJ0PnHSOfTI=
|
|
||||||
github.com/go-zoox/ini v1.0.4 h1:N4mUbAO0juYIRrv3ysjKtpEn/+yQv57eQietsgpkAYQ=
|
|
||||||
github.com/go-zoox/ini v1.0.4/go.mod h1:SisQneNLb1EBeZ5bA5GnrJd8FNg372hQrPh+gb3IzV4=
|
|
||||||
github.com/go-zoox/jobqueue v1.0.0 h1:pVv/eGI0CLLHUP3rDVyn0ALzsobtaxTOnkWw/JhW9Vg=
|
|
||||||
github.com/go-zoox/jobqueue v1.0.0/go.mod h1:jUCZxrQcM28orhac67eNLU7SBiVNXehxSelj7j4MM88=
|
|
||||||
github.com/go-zoox/jsonrpc v1.2.2 h1:asaoJgJkfyH5eblLQ1WzrZDe8ERL6v9GT4pKR/LJ3IE=
|
|
||||||
github.com/go-zoox/jsonrpc v1.2.2/go.mod h1:HdxJW/T0hkVHlfm+ULRnNEqvTtvZ7o4qxdQGQW76khM=
|
|
||||||
github.com/go-zoox/jwt v1.0.0/go.mod h1:a6ANQHmSs+b9GJv5aad2cQLl8opFmP3hMOxZtgXRmis=
|
|
||||||
github.com/go-zoox/jwt v1.3.0 h1:beyPOdiiNrNK8dqFijt5kdtaeh1dZKtM7/kaCMGbV0U=
|
|
||||||
github.com/go-zoox/jwt v1.3.0/go.mod h1:Cfc+t0XhNCgDjXLR5sK6ao7qz1GSIq896gZ1usNb7t8=
|
|
||||||
github.com/go-zoox/kv v1.4.1/go.mod h1:dc3whoIvGrYmQA2wi6g6ZE0oOtRg+loxaJEj6bLKlJA=
|
|
||||||
github.com/go-zoox/kv v1.4.3/go.mod h1:hRCBcPBHilKmeSEsn4o67LBaXurX0+m3Tq9Ec4aIRWk=
|
|
||||||
github.com/go-zoox/kv v1.5.9 h1:xEjIRJVcwIg2PhO0ZL+KWR5mCI/f5VshmcOjZPd4FRA=
|
|
||||||
github.com/go-zoox/kv v1.5.9/go.mod h1:sD2FmWrme1gzWaLciBAPyK0BtW3BluM02UGskOqf2MA=
|
|
||||||
github.com/go-zoox/logger v1.2.0/go.mod h1:mBImRV6zpbGtiIjDz/C9vWi80wWc2OTOl9N9P0SAJgk=
|
|
||||||
github.com/go-zoox/logger v1.4.6 h1:zHUaB6KQ9rD/N3hM0JJ3/JCNdgtedf4mVBBNNSyWCOg=
|
|
||||||
github.com/go-zoox/logger v1.4.6/go.mod h1:o7ddvv/gMoMa0TomPhHoIz11ZWRbQ92pF6rwYbOY3iQ=
|
|
||||||
github.com/go-zoox/lru v1.0.1 h1:AvRHxKEeEFSH9UXyfDQ5lj8nr66p6tJS3kVf/eTVyMg=
|
|
||||||
github.com/go-zoox/lru v1.0.1/go.mod h1:xxtYsRbJ2iJKEL4OIEZ6lk0xjuGJHHZmZZhGtH5qwv0=
|
|
||||||
github.com/go-zoox/mq v1.0.1 h1:JZSgWfp4JJDVKN8FgUkWWNDb3HOhV15dIdi+ecZENwQ=
|
|
||||||
github.com/go-zoox/mq v1.0.1/go.mod h1:0Zhgww1wcFNC37NJZjtumai03MvBAZuQ4VhezcAiFJE=
|
|
||||||
github.com/go-zoox/openai-client v1.5.2 h1:oOxXRb+0H0e59SfXz23zVZtP/G/FBuNZeOws3qTjE10=
|
|
||||||
github.com/go-zoox/openai-client v1.5.2/go.mod h1:h6vfyPajS1ScdALzDb+CwcSiMH3WKqScma0qH+SlRF8=
|
|
||||||
github.com/go-zoox/proxy v1.2.3/go.mod h1:T+gbngAtIgvambbYibAbzrjwdu8j5pduJdA2j6RYi94=
|
|
||||||
github.com/go-zoox/proxy v1.5.6 h1:Ha5wsSjIi57TcYJnb4iBrW1xmJlNW2E7dWjUIwIe6iE=
|
|
||||||
github.com/go-zoox/proxy v1.5.6/go.mod h1:KLWeJqfQk1upCvEdXt3tEuM8xSu0ApbA9FNLOmyHysY=
|
|
||||||
github.com/go-zoox/pubsub v1.2.2 h1:dpcFlZRSGhX0YqT/WoOJgP5bP2VDqswPqXiiFZCki1w=
|
|
||||||
github.com/go-zoox/pubsub v1.2.2/go.mod h1:LWX0NAg80hkeGdZf7PJOEGnyN6CXooCxpIlh2MxESDo=
|
|
||||||
github.com/go-zoox/random v1.0.0/go.mod h1:W+PTQiInxaCngiXpSvycucAKvu1tE/tKlZ9kaMp2/Ys=
|
|
||||||
github.com/go-zoox/random v1.0.4 h1:icckpkCowQ0eGiiMkHFOJz9Qc9noOcinP+ggqWUIBH4=
|
|
||||||
github.com/go-zoox/random v1.0.4/go.mod h1:W+PTQiInxaCngiXpSvycucAKvu1tE/tKlZ9kaMp2/Ys=
|
|
||||||
github.com/go-zoox/ratelimit v1.0.1/go.mod h1:5MtLMrfQRbZHI+tKC4eyHZorrZX005Sy/Dldnk8qYOU=
|
|
||||||
github.com/go-zoox/ratelimit v1.2.1 h1:iFuD6Md2jDERFXF2oiAzyt+px+SIOu+ICHIoB7v2KgY=
|
|
||||||
github.com/go-zoox/ratelimit v1.2.1/go.mod h1:CyFcL4Cpm7O8jvry9O+AlxyrgJGgvl4ebo4j0D3CGHA=
|
|
||||||
github.com/go-zoox/retry v1.0.3 h1:qpGq2Dqe9/mjhryDDwMEFWN+YDLHvpXL5hfwKJ1yTC4=
|
|
||||||
github.com/go-zoox/retry v1.0.3/go.mod h1:4NJ0wCtxGgHwzHYIN4bG1VnQ19w3rY2kzyOpNfm7x5o=
|
|
||||||
github.com/go-zoox/safe v1.0.1 h1:JwWK7xCyv7eyzBbwzQvhK/Ajm8gG2Q9Cvd/KXpdF2zI=
|
|
||||||
github.com/go-zoox/safe v1.0.1/go.mod h1:lT0iEBmpoia7xfh2bWzdurzdv++4QRUAfeEzIhCnpoA=
|
|
||||||
github.com/go-zoox/session v1.2.0 h1:hzjcZYV3+cVmocnC6MazhtELONMtRlIuEezCYvbxY9Q=
|
|
||||||
github.com/go-zoox/session v1.2.0/go.mod h1:SLHzCK3DDknqot28deZQFBPz5hm9QHcUeRra8y9GT8E=
|
|
||||||
github.com/go-zoox/tag v1.0.2/go.mod h1:TNFY+IN6FgsD0KGV6mrnuVXONUq7zpH01k53BcNsT8k=
|
|
||||||
github.com/go-zoox/tag v1.0.6/go.mod h1:jrbJgC1dZAN5+vZlmrUKu1/UpbOo0xVyCC1MfLpGGqk=
|
|
||||||
github.com/go-zoox/tag v1.0.9/go.mod h1:jrbJgC1dZAN5+vZlmrUKu1/UpbOo0xVyCC1MfLpGGqk=
|
|
||||||
github.com/go-zoox/tag v1.1.0/go.mod h1:yMB7bMseqbOshUW9O9Dqfq0C7Mmy9OkccV/meEJHICs=
|
|
||||||
github.com/go-zoox/tag v1.2.3 h1:HDQpRu8rA1xSJt6c+v0O7TfzTjPq5aDtyzW/15aTh94=
|
|
||||||
github.com/go-zoox/tag v1.2.3/go.mod h1:z9z4iZb/XPE4HwTXJgPIdwgH90c2NysGxIMq9tW+GuU=
|
|
||||||
github.com/go-zoox/testify v1.0.2 h1:G5sQ3xm0uwCuytnMhgnqZ5BItCt2DN3n2wLBqlIJEWA=
|
|
||||||
github.com/go-zoox/uuid v0.0.1 h1:txqmDavRTq68gzzqWfJQLorFyUp9a7M2lmq2KcwPGPA=
|
|
||||||
github.com/go-zoox/uuid v0.0.1/go.mod h1:0/F4LdfLqFdyqOf7aXoiYXRkXHU324JQ5DZEytXYBPM=
|
|
||||||
github.com/go-zoox/zoox v1.2.19/go.mod h1:xk3S3L58ugJIDyuZMCYrj3qIGLSxddbkARwTRkpxPVE=
|
|
||||||
github.com/go-zoox/zoox v1.12.29 h1:eg4ug+sXvWt2w9KEaSyrV4Fzv0QE4dcCweKApcu2ZJY=
|
|
||||||
github.com/go-zoox/zoox v1.12.29/go.mod h1:Z8VmwMC7mahiXErAvy8GXv+tM8LHT0YPNOGLSycGTYw=
|
|
||||||
github.com/go-zoox/zoox v1.12.37 h1:8gQfx2ZR6UN6A9oSIQll/rHf1KBGV5YDD5iILhpZl0Q=
|
|
||||||
github.com/go-zoox/zoox v1.12.37/go.mod h1:kTJ7oHR9fui8FO06BWA1DC3fouAYCzpdQgwmopAPh10=
|
|
||||||
github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA=
|
|
||||||
github.com/goccy/go-yaml v1.11.2 h1:joq77SxuyIs9zzxEjgyLBugMQ9NEgTWxXfz2wVqwAaQ=
|
|
||||||
github.com/goccy/go-yaml v1.11.2/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU=
|
|
||||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
|
||||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
|
||||||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
|
||||||
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
|
||||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
|
||||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
|
||||||
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
|
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
|
||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
|
||||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
|
||||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
|
||||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
|
||||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
|
||||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
|
||||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
|
||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.13/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
|
||||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
|
||||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
|
||||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
|
||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
|
||||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
|
||||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
|
||||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
|
||||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
|
||||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
|
||||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
|
||||||
github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
|
||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
|
||||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
|
||||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
|
||||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
|
||||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
|
||||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
|
||||||
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
|
|
||||||
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
|
||||||
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
|
||||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
|
||||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
|
||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|
||||||
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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0=
|
|
||||||
github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
|
||||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
|
||||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
|
||||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
|
||||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
|
||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
|
||||||
github.com/sevlyar/go-daemon v0.1.6 h1:EUh1MDjEM4BI109Jign0EaknA2izkOyi0LV3ro3QQGs=
|
|
||||||
github.com/sevlyar/go-daemon v0.1.6/go.mod h1:6dJpPatBT9eUwM5VCw9Bt6CdX9Tk6UWvhW3MebLDRKE=
|
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
|
||||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
|
||||||
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
|
|
||||||
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
|
|
||||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
|
||||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
|
||||||
github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
|
||||||
github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
|
||||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
|
||||||
github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
|
|
||||||
github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
|
||||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
|
||||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
|
||||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
|
||||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
|
||||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
|
||||||
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31 h1:OXcKh35JaYsGMRzpvFkLv/MEyPuL49CThT1pZ8aSml4=
|
|
||||||
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
|
|
||||||
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
|
||||||
golang.org/x/crypto v0.0.0-20220313003712-b769efc7c000/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
|
||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
|
||||||
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
|
|
||||||
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
|
|
||||||
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
|
|
||||||
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
|
||||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
|
||||||
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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
|
||||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
|
||||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
|
||||||
golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
|
||||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
|
||||||
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
|
|
||||||
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
|
|
||||||
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
|
||||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
|
||||||
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-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/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-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/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-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
|
||||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
|
||||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
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.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
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.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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
|
||||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
|
||||||
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.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
|
||||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
|
||||||
golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
|
|
||||||
golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
|
|
||||||
golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
|
|
||||||
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
|
||||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
|
|
||||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
|
||||||
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.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
|
||||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
|
||||||
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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
|
64
logs.go
64
logs.go
@ -1,64 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/go-zoox/core-utils/fmt"
|
|
||||||
"github.com/go-zoox/fs"
|
|
||||||
"github.com/go-zoox/logger"
|
|
||||||
"github.com/go-zoox/logger/components/transport"
|
|
||||||
"github.com/go-zoox/logger/transport/file"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Logs struct {
|
|
||||||
Dir string
|
|
||||||
Level string
|
|
||||||
|
|
||||||
//
|
|
||||||
accessLogFile *os.File
|
|
||||||
errorLogFile *os.File
|
|
||||||
debugLogFile *os.File
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Logs) Setup() (err error) {
|
|
||||||
if ok := fs.IsExist(l.Dir); !ok {
|
|
||||||
if err := fs.Mkdirp(l.Dir); err != nil {
|
|
||||||
return fmt.Errorf("failed to create log directory for : %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
accessLog := fs.JoinPath(l.Dir, "access.log")
|
|
||||||
if l.accessLogFile, err = fs.Open(accessLog); err != nil {
|
|
||||||
return fmt.Errorf("failed to open access log(%s): %v", accessLog, err)
|
|
||||||
}
|
|
||||||
errorLog := fs.JoinPath(l.Dir, "error.log")
|
|
||||||
if l.errorLogFile, err = fs.Open(errorLog); err != nil {
|
|
||||||
return fmt.Errorf("failed to open error log(%s): %v", accessLog, err)
|
|
||||||
}
|
|
||||||
debugLog := fs.JoinPath(l.Dir, "debug.log")
|
|
||||||
if l.debugLogFile, err = fs.Open(debugLog); err != nil {
|
|
||||||
return fmt.Errorf("failed to open debug log(%s): %v", accessLog, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.AppendTransports(map[string]transport.Transport{
|
|
||||||
"access": file.New(&file.Config{
|
|
||||||
Level: "info",
|
|
||||||
File: l.accessLogFile,
|
|
||||||
Exact: true,
|
|
||||||
}),
|
|
||||||
"error": file.New(&file.Config{
|
|
||||||
Level: "error",
|
|
||||||
File: l.errorLogFile,
|
|
||||||
Exact: true,
|
|
||||||
}),
|
|
||||||
"debug": file.New(&file.Config{
|
|
||||||
Level: "debug",
|
|
||||||
File: l.debugLogFile,
|
|
||||||
Exact: true,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
|
|
||||||
logger.SetLevel(l.Level)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
202
main.go
202
main.go
@ -1,202 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/go-zoox/chatgpt-for-chatbot-feishu/config"
|
|
||||||
"github.com/go-zoox/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
app := cli.NewSingleProgram(&cli.SingleProgramConfig{
|
|
||||||
Name: "chatgpt-for-chatbot-feishu",
|
|
||||||
Usage: "chatgpt-for-chatbot-feishu is a portable chatgpt server",
|
|
||||||
Version: Version,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
&cli.IntFlag{
|
|
||||||
Name: "port",
|
|
||||||
Usage: "server port",
|
|
||||||
Aliases: []string{"p"},
|
|
||||||
EnvVars: []string{"PORT"},
|
|
||||||
Value: 8080,
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "api-path",
|
|
||||||
Usage: "custom api path, default: /",
|
|
||||||
EnvVars: []string{"API_PATH"},
|
|
||||||
Value: "/",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "openai-api-key",
|
|
||||||
Usage: "OpenAI API Key",
|
|
||||||
EnvVars: []string{"OPENAI_API_KEY"},
|
|
||||||
// Required: true,
|
|
||||||
},
|
|
||||||
&cli.Int64Flag{
|
|
||||||
Name: "openai-api-timeout",
|
|
||||||
Usage: "OpenAI API Timeout, unit: second, default: 300",
|
|
||||||
EnvVars: []string{"OPENAI_API_TIMEOUT"},
|
|
||||||
Value: 300,
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "openai-api-server",
|
|
||||||
Usage: "OpenAI API Server",
|
|
||||||
EnvVars: []string{"OPENAI_API_SERVER"},
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "openai-api-type",
|
|
||||||
Usage: "OpenAI API Type",
|
|
||||||
EnvVars: []string{"OPENAI_API_TYPE"},
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "openai-azure-resource",
|
|
||||||
Usage: "Azure OpenAI Service Resource",
|
|
||||||
EnvVars: []string{"OPENAI_AZURE_RESOURCE"},
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "openai-azure-deployment",
|
|
||||||
Usage: "Azure OpenAI Service Deployment",
|
|
||||||
EnvVars: []string{"OPENAI_AZURE_DEPLOYMENT"},
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "openai-azure-api-version",
|
|
||||||
Usage: "Azure OpenAI Service API Version",
|
|
||||||
EnvVars: []string{"OPENAI_AZURE_API_VERSION"},
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "app-id",
|
|
||||||
Usage: "Feishu App ID",
|
|
||||||
EnvVars: []string{"APP_ID"},
|
|
||||||
Required: true,
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "app-secret",
|
|
||||||
Usage: "Feishu App SECRET",
|
|
||||||
EnvVars: []string{"APP_SECRET"},
|
|
||||||
Required: true,
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "encrypt-key",
|
|
||||||
Usage: "enable encryption if you need",
|
|
||||||
EnvVars: []string{"ENCRYPT_KEY"},
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "verification-token",
|
|
||||||
Usage: "enable token verification if you need",
|
|
||||||
EnvVars: []string{"VERIFICATION_TOKEN"},
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "report-url",
|
|
||||||
Usage: "Set error report url",
|
|
||||||
EnvVars: []string{"REPORT_URL"},
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "site-url",
|
|
||||||
Usage: "The Site URL",
|
|
||||||
EnvVars: []string{"SITE_URL"},
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "openai-model",
|
|
||||||
Usage: "Custom open ai model",
|
|
||||||
EnvVars: []string{"OPENAI_MODEL"},
|
|
||||||
},
|
|
||||||
&cli.Float64Flag{
|
|
||||||
Name: "openai-temperature",
|
|
||||||
Usage: "Custom open ai temperature",
|
|
||||||
EnvVars: []string{"OPENAI_TEMPERATURE"},
|
|
||||||
Value: 0.3,
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "feishu-base-uri",
|
|
||||||
Usage: "Custom feishu base uri for selfhosted Feishu",
|
|
||||||
EnvVars: []string{"FEISHU_BASE_URI"},
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "conversation-context",
|
|
||||||
Usage: "Custom chatgpt conversation context",
|
|
||||||
EnvVars: []string{"CONVERSATION_CONTEXT"},
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "conversation-language",
|
|
||||||
Usage: "Custom chatgpt conversation lanuage",
|
|
||||||
EnvVars: []string{"CONVERSATION_LANGUAGE"},
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "logs-dir",
|
|
||||||
Usage: "The logs dir for save logs",
|
|
||||||
EnvVars: []string{"LOGS_DIR"},
|
|
||||||
Value: "/tmp/chatgpt-for-chatbot-feishu",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "logs-level",
|
|
||||||
Usage: "The logs level",
|
|
||||||
EnvVars: []string{"LOGS_LEVEL", "LOG_LEVEL"},
|
|
||||||
Value: "INFO",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "offline-message",
|
|
||||||
Usage: "The message to use for offline status",
|
|
||||||
EnvVars: []string{"OFFLINE_MESSAGE"},
|
|
||||||
Value: "robot is offline",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "admin-email",
|
|
||||||
Usage: "Sets the admin with admin email, who can run commands",
|
|
||||||
EnvVars: []string{"ADMIN_EMAIL"},
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "bot-name",
|
|
||||||
Usage: "Sets the bot name, default: ChatGPT",
|
|
||||||
EnvVars: []string{"BOT_NAME"},
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "proxy",
|
|
||||||
Usage: "Sets the request proxy",
|
|
||||||
EnvVars: []string{"PROXY", "HTTPS_PROXY"},
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "custom-command",
|
|
||||||
Usage: "Custom command, such as: doc => trigger /doc",
|
|
||||||
EnvVars: []string{"CUSTOM_COMMAND"},
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "custom-command-service",
|
|
||||||
Usage: "Custom command service, such as: https://example.com/api/doc",
|
|
||||||
EnvVars: []string{"CUSTOM_COMMAND_SERVICE"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
app.Command(func(ctx *cli.Context) (err error) {
|
|
||||||
return ServeFeishuBot(&config.Config{
|
|
||||||
LogsDir: ctx.String("logs-dir"),
|
|
||||||
LogsLevel: ctx.String("logs-level"),
|
|
||||||
Port: ctx.Int64("port"),
|
|
||||||
APIPath: ctx.String("api-path"),
|
|
||||||
OpenAIAPIKey: ctx.String("openai-api-key"),
|
|
||||||
OpenAIAPITimeout: ctx.Int64("openai-api-timeout"),
|
|
||||||
AppID: ctx.String("app-id"),
|
|
||||||
AppSecret: ctx.String("app-secret"),
|
|
||||||
EncryptKey: ctx.String("encrypt-key"),
|
|
||||||
VerificationToken: ctx.String("verification-token"),
|
|
||||||
ReportURL: ctx.String("report-url"),
|
|
||||||
SiteURL: ctx.String("site-url"),
|
|
||||||
OpenAIModel: ctx.String("openai-model"),
|
|
||||||
OpenAITemperature: ctx.Float64("openai-temperature"),
|
|
||||||
FeishuBaseURI: ctx.String("feishu-base-uri"),
|
|
||||||
ConversationContext: ctx.String("conversation-context"),
|
|
||||||
ConversationLanguage: ctx.String("conversation-language"),
|
|
||||||
OfflineMessage: ctx.String("offline-message"),
|
|
||||||
AdminEmail: ctx.String("admin-email"),
|
|
||||||
BotName: ctx.String("bot-name"),
|
|
||||||
Proxy: ctx.String("proxy"),
|
|
||||||
OpenAIAPIServer: ctx.String("openai-api-server"),
|
|
||||||
OpenAIAPIType: ctx.String("openai-api-type"),
|
|
||||||
OpenAIAzureResource: ctx.String("openai-azure-resource"),
|
|
||||||
OpenAIAzureDeployment: ctx.String("openai-azure-deployment"),
|
|
||||||
OpenAIAzureAPIVersion: ctx.String("openai-azure-api-version"),
|
|
||||||
CustomCommand: ctx.String("custom-command"),
|
|
||||||
CustomCommandService: ctx.String("custom-command-service"),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
app.Run()
|
|
||||||
}
|
|
184
server.go
184
server.go
@ -1,184 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/go-zoox/chalk"
|
|
||||||
"github.com/go-zoox/chatbot-feishu"
|
|
||||||
"github.com/go-zoox/chatgpt-for-chatbot-feishu/commands"
|
|
||||||
"github.com/go-zoox/chatgpt-for-chatbot-feishu/config"
|
|
||||||
openaiclient "github.com/go-zoox/openai-client"
|
|
||||||
"github.com/go-zoox/zoox"
|
|
||||||
"github.com/go-zoox/zoox/defaults"
|
|
||||||
|
|
||||||
"github.com/go-zoox/core-utils/fmt"
|
|
||||||
"github.com/go-zoox/feishu"
|
|
||||||
|
|
||||||
chatgpt "github.com/go-zoox/chatgpt-client"
|
|
||||||
feishuEvent "github.com/go-zoox/feishu/event"
|
|
||||||
"github.com/go-zoox/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ServeFeishuBot(cfg *config.Config) (err error) {
|
|
||||||
if cfg.OfflineMessage == "" {
|
|
||||||
cfg.OfflineMessage = "robot is offline"
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.OpenAIModel == "" {
|
|
||||||
cfg.OpenAIModel = openaiclient.ModelGPT_4
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Infof("###### Settings START #######")
|
|
||||||
logger.Infof("Serve Version: %s", Version)
|
|
||||||
logger.Infof("Serve at PORT: %d", cfg.Port)
|
|
||||||
logger.Infof("Serve at API_PATH: %s", cfg.APIPath)
|
|
||||||
logger.Infof("###### Settings END #######")
|
|
||||||
|
|
||||||
logs := &Logs{
|
|
||||||
Dir: cfg.LogsDir,
|
|
||||||
Level: cfg.LogsLevel,
|
|
||||||
}
|
|
||||||
if err := logs.Setup(); err != nil {
|
|
||||||
return fmt.Errorf("failed to setup logs: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
chatgptClient, err := chatgpt.New(&chatgpt.Config{
|
|
||||||
APIKey: cfg.OpenAIAPIKey,
|
|
||||||
APIServer: cfg.OpenAIAPIServer,
|
|
||||||
APIType: cfg.OpenAIAPIType,
|
|
||||||
AzureResource: cfg.OpenAIAzureResource,
|
|
||||||
AzureDeployment: cfg.OpenAIAzureDeployment,
|
|
||||||
AzureAPIVersion: cfg.OpenAIAzureAPIVersion,
|
|
||||||
ConversationContext: cfg.ConversationContext,
|
|
||||||
ConversationLanguage: cfg.ConversationLanguage,
|
|
||||||
ChatGPTName: cfg.BotName,
|
|
||||||
Proxy: cfg.Proxy,
|
|
||||||
Timeout: time.Duration(cfg.OpenAIAPITimeout) * time.Second,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to create chatgpt client: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
feishuClient := feishu.New(&feishu.Config{
|
|
||||||
AppID: cfg.AppID,
|
|
||||||
AppSecret: cfg.AppSecret,
|
|
||||||
BaseURI: cfg.FeishuBaseURI,
|
|
||||||
})
|
|
||||||
|
|
||||||
cfg.Version = Version
|
|
||||||
cfg.IsInService = true
|
|
||||||
|
|
||||||
tryToGetBotInfo := func() {
|
|
||||||
for {
|
|
||||||
if cfg.BotInfo != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Infof("Trying to get bot info ...")
|
|
||||||
cfg.BotInfo, err = feishuClient.Bot().GetBotInfo()
|
|
||||||
if err != nil {
|
|
||||||
logger.Errorf("failed to get bot info: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Infof("Bot Name: %s", cfg.BotInfo.AppName)
|
|
||||||
logger.Infof("Feishu Bot Online ...")
|
|
||||||
time.Sleep(3 * time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
tryToGetBotInfo()
|
|
||||||
}()
|
|
||||||
|
|
||||||
fmt.PrintJSON(map[string]interface{}{
|
|
||||||
"version": Version,
|
|
||||||
"cfg": cfg,
|
|
||||||
})
|
|
||||||
|
|
||||||
if cfg.SiteURL != "" {
|
|
||||||
logger.Infof("")
|
|
||||||
logger.Infof("###### Feishu Configuration START #######")
|
|
||||||
logger.Infof("# %s:%s", chalk.Red("飞书事件订阅请求地址"), chalk.Green(fmt.Sprintf("%s%s", cfg.SiteURL, cfg.APIPath)))
|
|
||||||
logger.Infof("###### Feishu Configuration END #######")
|
|
||||||
logger.Infof("")
|
|
||||||
}
|
|
||||||
|
|
||||||
feishuchatbot, err := chatbot.New(&chatbot.Config{
|
|
||||||
Port: cfg.Port,
|
|
||||||
Path: cfg.APIPath,
|
|
||||||
AppID: cfg.AppID,
|
|
||||||
AppSecret: cfg.AppSecret,
|
|
||||||
VerificationToken: cfg.VerificationToken,
|
|
||||||
EncryptKey: cfg.EncryptKey,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to create feishu chatbot: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
feishuchatbot.OnCommand("ping", commands.CreatePingCommand(feishuClient, chatgptClient))
|
|
||||||
|
|
||||||
feishuchatbot.OnCommand("offline", commands.CreateOfflineCommand(feishuClient, chatgptClient, cfg))
|
|
||||||
|
|
||||||
feishuchatbot.OnCommand("online", commands.CreateOnlineCommand(feishuClient, chatgptClient, cfg))
|
|
||||||
|
|
||||||
feishuchatbot.OnCommand("model", commands.CreateModelCommand(feishuClient, chatgptClient, cfg))
|
|
||||||
|
|
||||||
feishuchatbot.OnCommand("reset", commands.CreateResetCommand(feishuClient, chatgptClient, cfg))
|
|
||||||
|
|
||||||
feishuchatbot.OnCommand("message", commands.CreateMessageCommand(feishuClient, chatgptClient, cfg))
|
|
||||||
feishuchatbot.OnCommand("问答", commands.CreateMessageCommand(feishuClient, chatgptClient, cfg))
|
|
||||||
|
|
||||||
feishuchatbot.OnCommand("draw", commands.CreateDrawCommand(feishuClient, chatgptClient))
|
|
||||||
feishuchatbot.OnCommand("画图", commands.CreateDrawCommand(feishuClient, chatgptClient))
|
|
||||||
|
|
||||||
if cfg.CustomCommand != "" && cfg.CustomCommandService != "" {
|
|
||||||
feishuchatbot.OnCommand(cfg.CustomCommand, commands.CreateCustomCommand(feishuClient, chatgptClient, cfg))
|
|
||||||
}
|
|
||||||
|
|
||||||
feishuchatbot.OnMessage(func(text string, request *feishuEvent.EventRequest, reply func(content string, msgType ...string) error) error {
|
|
||||||
return commands.
|
|
||||||
CreateMessageCommand(feishuClient, chatgptClient, cfg).
|
|
||||||
Handler([]string{text}, request, reply)
|
|
||||||
})
|
|
||||||
|
|
||||||
// return feishuchatbot.Run()
|
|
||||||
|
|
||||||
return run(
|
|
||||||
feishuchatbot,
|
|
||||||
cfg.Port,
|
|
||||||
cfg.APIPath,
|
|
||||||
cfg.OpenAIAPIKey,
|
|
||||||
cfg.OpenAIAPIServer,
|
|
||||||
cfg.OpenAIAPIType,
|
|
||||||
cfg.OpenAIAzureResource,
|
|
||||||
cfg.OpenAIAzureDeployment,
|
|
||||||
cfg.OpenAIAzureAPIVersion,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func run(
|
|
||||||
chatbot chatbot.ChatBot,
|
|
||||||
port int64,
|
|
||||||
path,
|
|
||||||
OpenAIAPIKey,
|
|
||||||
OpenAIAPIServer,
|
|
||||||
OpenAIAPIType,
|
|
||||||
OpenAIAzureResource,
|
|
||||||
OpenAIAzureDeployment,
|
|
||||||
OpenAIAzureAPIVersion string,
|
|
||||||
) error {
|
|
||||||
if OpenAIAPIServer == "" {
|
|
||||||
OpenAIAPIServer = openaiclient.DefaultAPIServer
|
|
||||||
}
|
|
||||||
|
|
||||||
app := defaults.Application()
|
|
||||||
|
|
||||||
app.Post(path, chatbot.Handler())
|
|
||||||
|
|
||||||
app.Get(path, func(ctx *zoox.Context) {
|
|
||||||
ctx.String(200, "OK")
|
|
||||||
})
|
|
||||||
|
|
||||||
return app.Run(fmt.Sprintf(":%d", port))
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
// Version is the version of lighthouse
|
|
||||||
var Version = "1.10.3"
|
|
Loading…
x
Reference in New Issue
Block a user