main
Your Name 2024-07-21 02:13:35 +08:00
commit 6274873ecd
18 changed files with 587 additions and 0 deletions

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/udesk.iml" filepath="$PROJECT_DIR$/.idea/udesk.iml" />
</modules>
</component>
</project>

9
.idea/udesk.iml Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

13
Dockerfile Normal file
View File

@ -0,0 +1,13 @@
FROM alpine:latest
ENV TZ=Asia/Shanghai
USER root
# 合并复制指令
COPY udeskstate /usr/local/bin/ \
./*.json /conf/
# 设置执行权限
RUN chmod +x /usr/local/bin/udeskstate
# ENTRYPOINT 指令只有一条,已经是最优
ENTRYPOINT ["udeskstate"]

18
email.json Normal file
View File

@ -0,0 +1,18 @@
{
"吴彬": "binwu@alauda.io",
"冯浪": "langfeng@alauda.io",
"李斌": "binli@alauda.io",
"杨学成": "xcyang@alauda.io",
"罗舒文": "swluo@alauda.io",
"杨慧": "huiyang1@alauda.io",
"吴舒汀": "stwu@alauda.io",
"王硕": "shuowang@alauda.io",
"崔星林": "xlcui@alauda.io",
"孙嘉彤": "jtsun@alauda.io",
"王奥": "aowang@alauda.io",
"霍佳琦": "jqhuo@alauda.io",
"王培伦": "plwang@alauda.io",
"李晓升": "xsli@alauda.io",
"孙凯": "kaisun@alauda.io",
"王杭渝": "hywang@alauda.io"
}

125
exceldeal/exceldeal.go Normal file
View File

@ -0,0 +1,125 @@
package exceldeal
import (
"encoding/json"
"github.com/xuri/excelize/v2"
"log"
"os"
"strings"
"time"
)
var nowMouth = time.Now().Month().String()
var nowDay = time.Now().Day()
var M = dateToChinese(nowMouth)
func JsonDeal(file string, remindList []string, closeList []string) (r, c []string) {
emailJson, err := os.ReadFile(file)
if err != nil {
log.Printf("打开邮箱json文件失败,错误是:%v\n", err)
return
}
var email map[string]string
err = json.Unmarshal(emailJson, &email)
if err != nil {
log.Printf("json反序列化失败:%v\n", err)
return nil, nil
}
for k, v := range email {
for _, m := range remindList {
if k == m {
r = append(r, v)
}
}
for _, m := range closeList {
if k == m {
c = append(c, v)
}
}
}
return r, c
}
// 寻找当前月的值班表,并且得到应该提醒的传值和关闭的传值
func GetSheetName(file string) (remindList, closeList []string, err error) {
f, err := excelize.OpenFile(file)
if err != nil {
return
}
defer f.Close()
//遍历所有工作表的A2格
sheets := f.GetSheetMap()
var sheetName string
for _, s := range sheets {
//// 读取A2单元格的值
a2value, err := f.GetCellValue(s, "A2")
if err != nil {
log.Println(err)
return nil, nil, err
}
//检查月份是否与当前月份相等
//如果与当前时间的月份相等则记录工作表名称
if a2value == M {
sheetName = s
}
}
rows, _ := f.GetRows(sheetName)
for i, row := range rows {
log.Printf("按行读取excel中的值:索引:%v,行值:%v\n", i, row)
if i == nowDay {
nowRow := cleanSlice(row)
remindList = append(remindList, nowRow[2], nowRow[3])
} else if i == nowDay-1 {
yesterdayRow := cleanSlice(row)
closeList = append(closeList, yesterdayRow[2], yesterdayRow[3])
}
}
return remindList, closeList, nil
}
func dateToChinese(m string) (M string) {
switch m {
case "January":
M = "一月"
case "February":
M = "二月"
case "March":
M = "三月"
case "April":
M = "四月"
case "May":
M = "五月"
case "June":
M = "六月"
case "July":
M = "七月"
case "August":
M = "八月"
case "September":
M = "九月"
case "October":
M = "十月"
case "November":
M = "十一月"
case "December":
M = "十二月"
}
return M
}
func cleanSlice(strSlice []string) (cleanedSlice []string) {
for _, s := range strSlice {
cleaned := strings.TrimSpace(s)
if cleaned != "" {
cleanedSlice = append(cleanedSlice, cleaned)
}
}
return cleanedSlice
}

56
exceldeal/notify.go Normal file
View File

@ -0,0 +1,56 @@
package exceldeal
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
)
var webhookURL = getWebhook()
type webHook struct {
WebHookURL string `json:"webhook"`
}
func getWebhook() string {
wb, err := os.ReadFile("./wechatwebhook.json")
fmt.Println(string(wb))
if err != nil {
log.Printf("打开webhook文件失败:%v\n", err)
return ""
}
var webhook webHook
err = json.Unmarshal(wb, &webhook)
if err != nil {
log.Printf("webhook config 反序列化失败: %v\n", err)
return ""
}
hook := webhook.WebHookURL
return hook
}
func Send(repContent string) {
// 替换为你的企业微信机器人Webhook URL
// 创建消息体
requestBody := []byte(`{
"msgtype": "text",
"text": {
"content": "` + repContent + `"
}
}`)
// 发送HTTP POST请求到企业微信机器人Webhook
resp, err := http.Post(webhookURL, "application/json", bytes.NewBuffer(requestBody))
if err != nil {
log.Fatalf("发送消息失败: %v", err)
return
}
defer resp.Body.Close()
log.Println("发送企业微信机器人成功!")
}

17
go.mod Normal file
View File

@ -0,0 +1,17 @@
module udesk
go 1.22
require (
github.com/google/uuid v1.6.0 // indirect
github.com/jasonlvhit/gocron v0.0.1 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/richardlehane/mscfb v1.0.4 // indirect
github.com/richardlehane/msoleps v1.0.3 // indirect
github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d // indirect
github.com/xuri/excelize/v2 v2.8.1 // indirect
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 // indirect
golang.org/x/crypto v0.25.0 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/text v0.16.0 // indirect
)

48
go.sum Normal file
View File

@ -0,0 +1,48 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-redis/redis v6.15.5+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jasonlvhit/gocron v0.0.1 h1:qTt5qF3b3srDjeOIR4Le1LfeyvoYzJlYpqvG7tJX5YU=
github.com/jasonlvhit/gocron v0.0.1/go.mod h1:k9a3TV8VcU73XZxfVHCHWMWF9SOqgoku0/QlY2yvlA4=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM=
github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d h1:llb0neMWDQe87IzJLS4Ci7psK/lVsjIS2otl+1WyRyY=
github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
github.com/xuri/excelize/v2 v2.8.1 h1:pZLMEwK8ep+CLIUWpWmvW8IWE/yxqG0I1xcN6cVMGuQ=
github.com/xuri/excelize/v2 v2.8.1/go.mod h1:oli1E4C3Pa5RXg1TBXn4ENCXDV5JUMlBluUhG7c+CEE=
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 h1:hPVCafDV85blFTabnqKgNhDCkJX25eik94Si9cTER4A=
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

51
main.go Normal file
View File

@ -0,0 +1,51 @@
package main
import (
"fmt"
"github.com/jasonlvhit/gocron"
"log"
"udesk/exceldeal"
"udesk/udesk/modifystate"
)
func main() {
log.Printf("准备执行...")
// 定义任务
//gocron.Every(5).Second().Do(job)
gocron.Every(1).Day().At("09:30").Do(job)
//gocron.Every(1).Minutes().Do(job)
// 开始定时任务
<-gocron.Start()
}
// 定义执行的job
func job() {
fmt.Println("任务开始")
remindList, closeList, err := exceldeal.GetSheetName("./standby.xlsx")
if err != nil {
log.Fatalf("打开值班表失败:%v", err)
}
log.Println("应在线人员名字:", remindList)
log.Println("应离线人员名字:", closeList)
remindEmail, closeEmail := exceldeal.JsonDeal("./email.json", remindList, closeList)
log.Println("应在线人员邮箱:", remindEmail)
log.Println("应离线人员邮箱:", closeEmail)
for _, rEmail := range closeEmail {
err = modifystate.PostToModifyState(rEmail, "offline")
if err != nil {
return
}
}
for _, rEmail := range remindEmail {
err = modifystate.PostToModifyState(rEmail, "idle")
if err != nil {
return
}
}
message := fmt.Sprintf("今日小雀: %v,备班1: %v\n昨日小雀: %v,备班1: %v\nudesk呼叫状态已修改。", remindList[0], remindList[1], closeList[0], closeList[1])
exceldeal.Send(message)
fmt.Println("任务结束")
}

BIN
standby.xlsx Normal file

Binary file not shown.

35
udesk/auth/base.go Normal file
View File

@ -0,0 +1,35 @@
package auth
const (
Email = "hbqi@alauda.io"
Sign_version = "v2"
Password = "Ye_qiu@123"
Auth_token_url = "https://servicecenter-alauda.udesk.cn/open_api_v1/log_in"
)
//获取鉴权token的账号密码请求体对象
type RequestUdeskBody struct {
Email string `json:"email"`
Password string `json:"password"`
}
//udesk鉴权接口返回token对象
type UdeskToken struct {
Code string `json:"code"`
Open_api_auth_token string `json:"open_api_auth_token"`
}
// 定义认证对象
type Authobj struct {
Email,
Timestamp,
Sign,
Nonce,
Sign_version string
}
//全局声明返回体对象
var u = newRespUdeskBody()

90
udesk/auth/function.go Normal file
View File

@ -0,0 +1,90 @@
package auth
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"github.com/google/uuid"
"io"
"net/http"
"strconv"
"time"
)
//新建请求体对象
func newReqUdeskToken() RequestUdeskBody {
return RequestUdeskBody{
Email: Email,
Password: Password,
}
}
// 新建返回体对象
func newRespUdeskBody() UdeskToken {
return UdeskToken{
Code: "",
Open_api_auth_token: "",
}
}
//获取Unix时间戳
func GetTimeStamp() string {
return strconv.FormatInt(time.Now().Unix(), 10)
}
//获取nonce
func GetNonce() string {
randomUUID := uuid.New()
nonce := randomUUID.String()
return nonce
}
// sha256转换函数
func calculateSHA256(input string) string {
// 将字符串转换为字节数组
inputBytes := []byte(input)
// 创建SHA-256哈希对象
hasher := sha256.New()
// 将字节数组写入哈希对象
hasher.Write(inputBytes)
// 计算哈希值并返回
hashInBytes := hasher.Sum(nil)
hashString := hex.EncodeToString(hashInBytes)
return hashString
}
// 获取鉴权token对象
func GetUdeskAuthToken() UdeskToken {
reqBody := newReqUdeskToken()
jsonData, err := json.Marshal(reqBody)
if err != nil {
fmt.Printf("获取udesk管理员token过程中转换请求体json失败错误是%v", err)
}
payload := bytes.NewBufferString(string(jsonData))
resp, err := http.Post(Auth_token_url, "application/json", payload)
if err != nil {
fmt.Printf("获取udesk管理员token过程中请求获取token接口失败错误是%v", err)
}
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
return
}
}(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Printf("获取udesk管理员token过程中获取token接口响应失败错误是%v", err)
}
json.Unmarshal(body, &u)
return u
}

17
udesk/auth/geturl.go Normal file
View File

@ -0,0 +1,17 @@
package auth
import "strings"
//最终请求的url后缀拼接
func Geturlstring(url string) string {
var authobj = u.getAuthobj() //必须要同步处理timestamp和sign的关系否则会鉴权失败所以从同一个结构体中取值
var builder strings.Builder
builder.WriteString(url)
builder.WriteString("email=" + authobj.Email)
builder.WriteString("&timestamp=" + authobj.Timestamp)
builder.WriteString("&sign=" + authobj.Sign)
builder.WriteString("&nonce=" + authobj.Nonce)
builder.WriteString("&sign_version=" + authobj.Sign_version)
return builder.String()
}

37
udesk/auth/method.go Normal file
View File

@ -0,0 +1,37 @@
package auth
import (
"strings"
)
//获取token字符串
func (UdeskToken) getTokenString() string {
token := GetUdeskAuthToken().Open_api_auth_token
return token
}
//计算sign并返回authobj
func (UdeskToken) getAuthobj() Authobj {
token := u.getTokenString()
timestamp := GetTimeStamp()
nonce := GetNonce()
var builder strings.Builder
builder.WriteString(Email + "&")
builder.WriteString(token + "&")
builder.WriteString(timestamp + "&")
builder.WriteString(nonce + "&")
builder.WriteString(Sign_version)
str2sha256 := builder.String()
hashResult := calculateSHA256(str2sha256)
//fmt.Println(str2sha256)
return Authobj{
Email: Email,
Timestamp: timestamp,
Sign: hashResult,
Nonce: nonce,
Sign_version: Sign_version,
}
}

View File

@ -0,0 +1,46 @@
package modifystate
import (
"fmt"
"io"
"log"
"net/http"
"strings"
"udesk/udesk/auth"
)
func PostToModifyState(email, state string) error {
// 获取 URL
url := fmt.Sprintf("https://servicecenter-alauda.udesk.cn/open_api_v1/callcenter/agent_state?email=%s&", email)
url = auth.Geturlstring(url)
// 请求体
bodystring := fmt.Sprintf("{\"agent_email\": \"%s\",\"agent_work_state\": \"%s\"}", email, state)
// 创建请求
req, err := http.NewRequest("POST", url, strings.NewReader(bodystring))
if err != nil {
return err
}
// 设置请求头
req.Header.Set("Content-Type", "application/json")
// 发送请求
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
// 读取响应体
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
// 打印响应体
log.Printf("send udesk to modify state,Response is: %s", string(respBody))
return nil
}

3
wechatwebhook.json Normal file
View File

@ -0,0 +1,3 @@
{
"webhook": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=9e27f428-7fb9-4771-8e48-231f59eb1ec4"
}