@@ -0,0 +1,262 @@
|
||||
package laosiji
|
||||
|
||||
import (
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/web/webg"
|
||||
"bytes"
|
||||
|
||||
"context"
|
||||
"crypto/aes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
Name string
|
||||
Appid string
|
||||
APIKey string
|
||||
APIUrl string
|
||||
IMAGEYUAN string
|
||||
NoticeURL string
|
||||
)
|
||||
|
||||
// Config 是老司机接口配置。所有凭证必须由各服务自己的配置文件注入。
|
||||
type Config struct {
|
||||
AppID string
|
||||
APIKey string
|
||||
APIUrl string
|
||||
ImageYuan string
|
||||
NoticeURL string
|
||||
}
|
||||
|
||||
func Init(lsjCfg webg.GlobalConfig) {
|
||||
InitConfig(Config{
|
||||
AppID: lsjCfg.LSJ.AppID,
|
||||
APIKey: lsjCfg.LSJ.APIKey,
|
||||
APIUrl: lsjCfg.LSJ.APIUrl,
|
||||
ImageYuan: lsjCfg.LSJ.ImageYuan,
|
||||
})
|
||||
}
|
||||
|
||||
// InitConfig 供不依赖 web 配置结构的服务(例如 skd)初始化老司机客户端。
|
||||
func InitConfig(cfg Config) {
|
||||
Name = "老司机"
|
||||
Appid = strings.TrimSpace(cfg.AppID)
|
||||
APIKey = strings.TrimSpace(cfg.APIKey)
|
||||
APIUrl = strings.TrimRight(strings.TrimSpace(cfg.APIUrl), "/")
|
||||
IMAGEYUAN = strings.TrimRight(strings.TrimSpace(cfg.ImageYuan), "/")
|
||||
NoticeURL = strings.TrimSpace(cfg.NoticeURL)
|
||||
}
|
||||
|
||||
// Configured 判断调用老司机接口所需的配置是否完整。
|
||||
func Configured() bool {
|
||||
return Appid != "" && APIKey != "" && APIUrl != ""
|
||||
}
|
||||
|
||||
func ensureConfigured() error {
|
||||
if Configured() {
|
||||
return nil
|
||||
}
|
||||
missing := make([]string, 0, 3)
|
||||
if Appid == "" {
|
||||
missing = append(missing, "appId")
|
||||
}
|
||||
if APIKey == "" {
|
||||
missing = append(missing, "apiKey")
|
||||
}
|
||||
if APIUrl == "" {
|
||||
missing = append(missing, "apiUrl")
|
||||
}
|
||||
return fmt.Errorf("laosiji configuration missing: %s", strings.Join(missing, ","))
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Status string `json:"status"`
|
||||
Data string `json:"data"` // 如果成功,返回的这个数据是加密的
|
||||
Time string `json:"time"`
|
||||
Error string `json:"error"`
|
||||
ErrorCode int `json:"errorCode"`
|
||||
}
|
||||
|
||||
func QueryUndress(ctx context.Context, taskID string) (resp QueryUndressResponse, err error) {
|
||||
endpoint := getQueryUrl()
|
||||
err = post(ctx, endpoint, map[string]interface{}{
|
||||
"task_id": taskID,
|
||||
}, &resp)
|
||||
if err != nil {
|
||||
log.Error("GenerateUndress post fail", log.Any("taskID", taskID), log.E(err))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GenerateUndress(ctx context.Context, req map[string]interface{}) (resp GenerateUndressResponse, err error) {
|
||||
endpoint := getAiUndressGenerateUrl()
|
||||
err = post(ctx, endpoint, req, &resp)
|
||||
if err != nil {
|
||||
log.Error("GenerateUndress post fail", log.Any("req", req), log.E(err))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func QueryTextToImage(ctx context.Context, taskID string) (resp QueryTextToImageResponse, err error) {
|
||||
endpoint := getQueryUrl()
|
||||
err = post(ctx, endpoint, map[string]interface{}{
|
||||
"task_id": taskID,
|
||||
}, &resp)
|
||||
if err != nil {
|
||||
log.Error("QueryTextToImage post fail", log.Any("taskID", taskID), log.E(err))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GenerateTextToImage(ctx context.Context, req map[string]interface{}) (resp GenerateTextToImageResponse, err error) {
|
||||
endpoint := getTextToImageGenerateUrl()
|
||||
err = post(ctx, endpoint, req, &resp)
|
||||
if err != nil {
|
||||
log.Error("GenerateTextToImage post fail", log.Any("req", req), log.E(err))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getQueryUrl() string {
|
||||
return fmt.Sprintf("%s/lsjapi/ai/detail", APIUrl)
|
||||
}
|
||||
|
||||
func getTextToImageGenerateUrl() string {
|
||||
return fmt.Sprintf("%s/lsjapi/ai/generate", APIUrl)
|
||||
}
|
||||
|
||||
func getAiUndressGenerateUrl() string {
|
||||
return fmt.Sprintf("%s/lsjapi/ai/undress", APIUrl)
|
||||
}
|
||||
|
||||
func post(ctx context.Context, endpoint string, req map[string]interface{}, response interface{}) (err error) {
|
||||
if err = ensureConfigured(); err != nil {
|
||||
return err
|
||||
}
|
||||
jsonData, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
log.Warn("GenerateAiUndress", log.E(err))
|
||||
return
|
||||
}
|
||||
encryptedData, err := encryptBase64(string(jsonData), APIKey)
|
||||
if err != nil {
|
||||
log.Warn("GenerateAiUndress encryptBase64", log.E(err))
|
||||
return
|
||||
}
|
||||
// 创建 POST 请求
|
||||
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewBuffer([]byte(encryptedData)))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
httpReq.Header.Set("Content-Type", "application/json")
|
||||
httpReq.Header.Set("Content-Length", fmt.Sprintf("%d", len(encryptedData)))
|
||||
httpReq.Header.Set("appid", Appid)
|
||||
|
||||
// 发送请求
|
||||
client := &http.Client{Timeout: 10 * time.Second}
|
||||
resp, err := client.Do(httpReq)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 读取响应体
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 检查 HTTP 状态码
|
||||
if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
|
||||
return fmt.Errorf("laosiji api http status:%d body:%s", resp.StatusCode, truncateToolBody(body, APIKey, Appid))
|
||||
}
|
||||
|
||||
// 解析 JSON
|
||||
var res Response
|
||||
if err = json.Unmarshal(body, &res); err != nil {
|
||||
return
|
||||
}
|
||||
if res.Status != "y" {
|
||||
err = fmt.Errorf("errorCode:%v error:%s", res.ErrorCode, truncateToolText(res.Error, APIKey, Appid))
|
||||
return
|
||||
}
|
||||
|
||||
// 进行解密
|
||||
dataStr, err := decryptBase64(res.Data, APIKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal([]byte(dataStr), response)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AES-128-ECB encrypt
|
||||
func encryptBase64(input, key string) (string, error) {
|
||||
if len(key) > 16 {
|
||||
key = key[:16]
|
||||
}
|
||||
plainText := []byte(input)
|
||||
keyBytes := []byte(key)
|
||||
block, err := aes.NewCipher(keyBytes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
blockSize := block.BlockSize()
|
||||
plainText = pkcs7Padding(plainText, blockSize)
|
||||
|
||||
encrypted := make([]byte, len(plainText))
|
||||
for bs, be := 0, blockSize; bs < len(plainText); bs, be = bs+blockSize, be+blockSize {
|
||||
block.Encrypt(encrypted[bs:be], plainText[bs:be])
|
||||
}
|
||||
|
||||
return base64.StdEncoding.EncodeToString(encrypted), nil
|
||||
}
|
||||
|
||||
// AES-128-ECB decrypt
|
||||
func decryptBase64(cipherText, key string) (string, error) {
|
||||
if len(key) > 16 {
|
||||
key = key[:16]
|
||||
}
|
||||
data, err := base64.StdEncoding.DecodeString(cipherText)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher([]byte(key))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
blockSize := block.BlockSize()
|
||||
decrypted := make([]byte, len(data))
|
||||
for bs, be := 0, blockSize; bs < len(data); bs, be = bs+blockSize, be+blockSize {
|
||||
block.Decrypt(decrypted[bs:be], data[bs:be])
|
||||
}
|
||||
|
||||
decrypted = pkcs7UnPadding(decrypted)
|
||||
return string(decrypted), nil
|
||||
}
|
||||
|
||||
// PKCS7Padding pads plaintext for AES ECB
|
||||
func pkcs7Padding(src []byte, blockSize int) []byte {
|
||||
padding := blockSize - len(src)%blockSize
|
||||
padText := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||
return append(src, padText...)
|
||||
}
|
||||
|
||||
// PKCS7UnPadding removes padding
|
||||
func pkcs7UnPadding(src []byte) []byte {
|
||||
length := len(src)
|
||||
unpadding := int(src[length-1])
|
||||
return src[:(length - unpadding)]
|
||||
}
|
||||
Reference in New Issue
Block a user