feat: migrate codes from previous project
This commit is contained in:
5
.env.example
Normal file
5
.env.example
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
FEISHU_APP_ID=
|
||||||
|
FEISHU_APP_SECRET=
|
||||||
|
FEISHU_CHAT_ID=
|
||||||
|
|
||||||
|
SILICON_TOKEN=
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -104,3 +104,4 @@ fabric.properties
|
|||||||
# Android studio 3.1+ serialized cache file
|
# Android studio 3.1+ serialized cache file
|
||||||
.idea/caches/build_file_checksums.ser
|
.idea/caches/build_file_checksums.ser
|
||||||
|
|
||||||
|
config/config.yaml
|
||||||
|
|||||||
5
.idea/inspectionProfiles/Project_Default.xml
generated
5
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -1,6 +1,11 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
<component name="InspectionProjectProfileManager">
|
||||||
<profile version="1.0">
|
<profile version="1.0">
|
||||||
<option name="myName" value="Project Default" />
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="GoResourceLeak" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<methods>
|
||||||
|
<method importPath="net/http" receiver="*Client" name="Do" />
|
||||||
|
</methods>
|
||||||
|
</inspection_tool>
|
||||||
<inspection_tool class="HtmlUnknownTag" enabled="true" level="WARNING" enabled_by_default="true">
|
<inspection_tool class="HtmlUnknownTag" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
<option name="myValues">
|
<option name="myValues">
|
||||||
<value>
|
<value>
|
||||||
|
|||||||
17
config/config.yaml.example
Normal file
17
config/config.yaml.example
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
templates:
|
||||||
|
prompts:
|
||||||
|
example: &ref_example ""
|
||||||
|
|
||||||
|
subscribes:
|
||||||
|
- name: ""
|
||||||
|
url: ""
|
||||||
|
prompt: *ref_example
|
||||||
|
model: ""
|
||||||
|
|
||||||
|
feishu:
|
||||||
|
app_id:
|
||||||
|
app_secret:
|
||||||
|
chat_id:
|
||||||
|
|
||||||
|
silicon:
|
||||||
|
token:
|
||||||
20
go.mod
20
go.mod
@@ -1,3 +1,23 @@
|
|||||||
module rss-to-feishu-next
|
module rss-to-feishu-next
|
||||||
|
|
||||||
go 1.24
|
go 1.24
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||||
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/gorilla/websocket v1.5.3 // indirect
|
||||||
|
github.com/larksuite/oapi-sdk-go/v3 v3.5.2 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
|
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||||
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||||
|
github.com/spf13/afero v1.15.0 // indirect
|
||||||
|
github.com/spf13/cast v1.10.0 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.10 // indirect
|
||||||
|
github.com/spf13/viper v1.21.0 // indirect
|
||||||
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
|
golang.org/x/sys v0.29.0 // indirect
|
||||||
|
golang.org/x/text v0.28.0 // indirect
|
||||||
|
)
|
||||||
|
|||||||
46
internal/config/config.go
Normal file
46
internal/config/config.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func LoadConfig() (*Config, error) {
|
||||||
|
v := viper.New()
|
||||||
|
|
||||||
|
v.AutomaticEnv()
|
||||||
|
|
||||||
|
v.SetConfigName("config")
|
||||||
|
v.SetConfigType("yaml")
|
||||||
|
v.AddConfigPath("./config")
|
||||||
|
v.AddConfigPath(".")
|
||||||
|
|
||||||
|
if err := v.ReadInConfig(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
v.SetConfigName(".env")
|
||||||
|
v.SetConfigType("env")
|
||||||
|
|
||||||
|
if err := v.MergeInConfig(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range v.AllKeys() {
|
||||||
|
if strings.Contains(key, "_") {
|
||||||
|
nestedKey := strings.Replace(key, "_", ".", 1)
|
||||||
|
|
||||||
|
if !v.IsSet(nestedKey) || v.Get(nestedKey) == "" {
|
||||||
|
v.Set(nestedKey, v.Get(key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var cfg Config
|
||||||
|
if err := v.Unmarshal(&cfg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &cfg, nil
|
||||||
|
}
|
||||||
24
internal/config/types.go
Normal file
24
internal/config/types.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Subscribes []Subscribe `mapstructure:"subscribes"`
|
||||||
|
Feishu Feishu `mapstructure:"feishu"`
|
||||||
|
Silicon Silicon `mapstructure:"silicon"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Subscribe struct {
|
||||||
|
Name string `mapstructure:"name"`
|
||||||
|
Url string `mapstructure:"url"`
|
||||||
|
Prompt string `mapstructure:"prompt"`
|
||||||
|
Model string `mapstructure:"model"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Feishu struct {
|
||||||
|
AppID string `mapstructure:"app_id"`
|
||||||
|
AppSecret string `mapstructure:"app_secret"`
|
||||||
|
ChatID string `mapstructure:"chat_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Silicon struct {
|
||||||
|
Token string `mapstructure:"token"`
|
||||||
|
}
|
||||||
53
internal/feishu/client.go
Normal file
53
internal/feishu/client.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package feishu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
lark "github.com/larksuite/oapi-sdk-go/v3"
|
||||||
|
larkcore "github.com/larksuite/oapi-sdk-go/v3/core"
|
||||||
|
larkim "github.com/larksuite/oapi-sdk-go/v3/service/im/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
larkClient *lark.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(larkClient *lark.Client) *Client {
|
||||||
|
return &Client{larkClient: larkClient}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) SendMessageToChat(ctx context.Context, message, chatID string) error {
|
||||||
|
content := &MessageContent{
|
||||||
|
Text: message,
|
||||||
|
}
|
||||||
|
contentJson, err := json.Marshal(content)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
contentString := string(contentJson)
|
||||||
|
|
||||||
|
req := larkim.NewCreateMessageReqBuilder().
|
||||||
|
ReceiveIdType("chat_id").
|
||||||
|
Body(larkim.NewCreateMessageReqBodyBuilder().
|
||||||
|
ReceiveId(chatID).
|
||||||
|
MsgType(`text`).
|
||||||
|
Content(contentString).
|
||||||
|
Uuid(uuid.New().String()).
|
||||||
|
Build()).
|
||||||
|
Build()
|
||||||
|
|
||||||
|
resp, err := c.larkClient.Im.V1.Message.Create(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !resp.Success() {
|
||||||
|
fmt.Printf("logId: %s, error response: \n%s", resp.RequestId(), larkcore.Prettify(resp.CodeError))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
5
internal/feishu/types.go
Normal file
5
internal/feishu/types.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package feishu
|
||||||
|
|
||||||
|
type MessageContent struct {
|
||||||
|
Text string `json:"text"`
|
||||||
|
}
|
||||||
71
internal/net/client.go
Normal file
71
internal/net/client.go
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
package net
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
client *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient() *Client {
|
||||||
|
return &Client{client: &http.Client{
|
||||||
|
Timeout: time.Minute * 5,
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Get(ctx context.Context, url string) (string, error) {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer func(Body io.ReadCloser) {
|
||||||
|
_ = Body.Close()
|
||||||
|
}(resp.Body)
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(body), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Post(ctx context.Context, headers map[string]string, url string, body any, target any) error {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := json.NewEncoder(&buf).Encode(body); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, &buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for k, v := range headers {
|
||||||
|
req.Header.Set(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func(Body io.ReadCloser) {
|
||||||
|
_ = Body.Close()
|
||||||
|
}(resp.Body)
|
||||||
|
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(target); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
48
internal/rss/client.go
Normal file
48
internal/rss/client.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package rss
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"rss-to-feishu-next/internal/config"
|
||||||
|
"rss-to-feishu-next/internal/net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
netClient *net.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(client *net.Client) *Client {
|
||||||
|
return &Client{netClient: client}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) FetchRss(ctx context.Context, name, url string) (*Message, error) {
|
||||||
|
resp, err := c.netClient.Get(ctx, url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Message{
|
||||||
|
Name: name,
|
||||||
|
Url: url,
|
||||||
|
Content: resp,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) FetchAllRss(ctx context.Context) ([]*Message, error) {
|
||||||
|
cfg, err := config.LoadConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var messages []*Message
|
||||||
|
subscribes := cfg.Subscribes
|
||||||
|
|
||||||
|
for i := 0; i < len(cfg.Subscribes); i++ {
|
||||||
|
message, err := c.FetchRss(ctx, subscribes[i].Name, subscribes[i].Url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
messages = append(messages, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
return messages, nil
|
||||||
|
}
|
||||||
7
internal/rss/types.go
Normal file
7
internal/rss/types.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package rss
|
||||||
|
|
||||||
|
type Message struct {
|
||||||
|
Name string
|
||||||
|
Url string
|
||||||
|
Content string
|
||||||
|
}
|
||||||
50
internal/silicon/client.go
Normal file
50
internal/silicon/client.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package silicon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"rss-to-feishu-next/internal/config"
|
||||||
|
"rss-to-feishu-next/internal/net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
netClient *net.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(client *net.Client) *Client {
|
||||||
|
return &Client{client}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) FetchModelResponse(ctx context.Context, message, model string) (*Response, error) {
|
||||||
|
cfg, err := config.LoadConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
token := cfg.Silicon.Token
|
||||||
|
|
||||||
|
headers := map[string]string{
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": "Bearer " + token,
|
||||||
|
}
|
||||||
|
|
||||||
|
var messages []*Message
|
||||||
|
messages = append(messages, &Message{
|
||||||
|
Role: "user",
|
||||||
|
Content: message,
|
||||||
|
})
|
||||||
|
|
||||||
|
body := &RequestBody{
|
||||||
|
Model: model,
|
||||||
|
Messages: messages,
|
||||||
|
Temperature: 0.7,
|
||||||
|
MaxTokens: 8096,
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp *Response
|
||||||
|
url := "https://api.siliconflow.cn/v1/messages"
|
||||||
|
if err := c.netClient.Post(ctx, headers, url, body, &resp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
21
internal/silicon/types.go
Normal file
21
internal/silicon/types.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package silicon
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Content []struct {
|
||||||
|
Text string `json:"text"`
|
||||||
|
} `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RequestBody struct {
|
||||||
|
Model string `json:"model"`
|
||||||
|
Messages []*Message `json:"messages"`
|
||||||
|
Temperature float64 `json:"temperature"`
|
||||||
|
MaxTokens int `json:"max_tokens"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Message struct {
|
||||||
|
Role string `json:"role"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
57
main.go
57
main.go
@@ -1,20 +1,57 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"rss-to-feishu-next/internal/config"
|
||||||
|
"rss-to-feishu-next/internal/feishu"
|
||||||
|
"rss-to-feishu-next/internal/net"
|
||||||
|
"rss-to-feishu-next/internal/rss"
|
||||||
|
"rss-to-feishu-next/internal/silicon"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
lark "github.com/larksuite/oapi-sdk-go/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TIP <p>To run your code, right-click the code and select <b>Run</b>.</p> <p>Alternatively, click
|
|
||||||
// the <icon src="AllIcons.Actions.Execute"/> icon in the gutter and select the <b>Run</b> menu item from here.</p>
|
|
||||||
func main() {
|
func main() {
|
||||||
//TIP <p>Press <shortcut actionId="ShowIntentionActions"/> when your caret is at the underlined text
|
cfg, err := config.LoadConfig()
|
||||||
// to see how GoLand suggests fixing the warning.</p><p>Alternatively, if available, click the lightbulb to view possible fixes.</p>
|
if err != nil {
|
||||||
s := "gopher"
|
log.Fatal(err)
|
||||||
fmt.Printf("Hello and welcome, %s!\n", s)
|
}
|
||||||
|
|
||||||
for i := 1; i <= 5; i++ {
|
ctx := context.Background()
|
||||||
//TIP <p>To start your debugging session, right-click your code in the editor and select the Debug option.</p> <p>We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
|
|
||||||
// for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>.</p>
|
netCli := net.NewClient()
|
||||||
fmt.Println("i =", 100/i)
|
rssCli := rss.NewClient(netCli)
|
||||||
|
siliconCli := silicon.NewClient(netCli)
|
||||||
|
|
||||||
|
larkCli := lark.NewClient(cfg.Feishu.AppID, cfg.Feishu.AppSecret)
|
||||||
|
feishuCli := feishu.NewClient(larkCli)
|
||||||
|
|
||||||
|
messages, err := rssCli.FetchAllRss(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(messages); i++ {
|
||||||
|
prompt := cfg.Subscribes[i].Prompt + "\n" + messages[i].Content
|
||||||
|
model := cfg.Subscribes[i].Model
|
||||||
|
resp, err := siliconCli.FetchModelResponse(ctx, prompt, model)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var parts []string
|
||||||
|
for _, item := range resp.Content {
|
||||||
|
parts = append(parts, item.Text)
|
||||||
|
}
|
||||||
|
|
||||||
|
allContent := strings.Join(parts, "\n")
|
||||||
|
|
||||||
|
message := fmt.Sprintf("%s\n%s\n%s", messages[i].Name, messages[i].Url, allContent)
|
||||||
|
if err := feishuCli.SendMessageToChat(ctx, message, cfg.Feishu.ChatID); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user