wechaty/go-wechaty

View on GitHub
wechaty-puppet-service/filebox.go

Summary

Maintainability
A
3 hrs
Test Coverage
F
0%
package puppetservice

import (
    "bytes"
    "errors"
    pbwechaty "github.com/wechaty/go-grpc/wechaty"
    pbwechatypuppet "github.com/wechaty/go-grpc/wechaty/puppet"
    "github.com/wechaty/go-wechaty/wechaty-puppet/filebox"
    "github.com/wechaty/go-wechaty/wechaty-puppet/helper"
    "io"
)

// ErrNoName err no name
var ErrNoName = errors.New("no name")

// NewFileBoxFromMessageFileStream ...
func NewFileBoxFromMessageFileStream(client pbwechaty.Puppet_MessageFileStreamClient) (*filebox.FileBox, error) {
    recv, err := client.Recv()
    if err != nil {
        return nil, err
    }
    name := recv.FileBoxChunk.GetName()
    if name == "" {
        return nil, ErrNoName
    }

    return filebox.FromStream(NewMessageFile(client), filebox.WithName(name)), nil
}

// MessageFile 把 grpc 流包装到 io.Reader 接口
type MessageFile struct {
    client pbwechaty.Puppet_MessageFileStreamClient
    buffer bytes.Buffer
    done   bool
}

// Read 把 grpc 流包装到 io.Reader 接口
func (m *MessageFile) Read(p []byte) (n int, err error) {
    if m.done {
        return m.buffer.Read(p)
    }

    for {
        if m.buffer.Len() >= len(p) {
            break
        }
        recv, err := m.client.Recv()
        if err == io.EOF {
            m.done = true
            break
        }
        if err != nil {
            return 0, err
        }
        _, err = m.buffer.Write(recv.FileBoxChunk.GetData())
        if err != nil {
            return 0, err
        }
    }
    return m.buffer.Read(p)
}

// NewMessageFile ...
func NewMessageFile(client pbwechaty.Puppet_MessageFileStreamClient) *MessageFile {
    return &MessageFile{
        client: client,
        buffer: bytes.Buffer{},
        done:   false,
    }
}

// MessageSendFile 把 grpc 流包装到 io.Writer 接口
type MessageSendFile struct {
    client  pbwechaty.Puppet_MessageSendFileStreamClient
    fileBox *filebox.FileBox
    count   int
}

// Write 把 grpc 流包装到 io.Writer 接口
func (m *MessageSendFile) Write(p []byte) (n int, err error) {
    if len(p) == 0 {
        return 0, nil
    }
    fileDataRequest := &pbwechatypuppet.MessageSendFileStreamRequest{
        FileBoxChunk: &pbwechatypuppet.FileBoxChunk{
            Data: p,
            Name: nil,
        },
    }
    m.count++
    if err := m.client.Send(fileDataRequest); err != nil {
        return 0, err
    }
    return len(p), nil
}

// ToMessageSendFileWriter 把 grpc 流包装到 io.Writer 接口
func ToMessageSendFileWriter(client pbwechaty.Puppet_MessageSendFileStreamClient, conversationID string, fileBox *filebox.FileBox) (io.Writer, error) {
    // 发送 conversationID
    {
        idRequest := &pbwechatypuppet.MessageSendFileStreamRequest{
            ConversationId: &conversationID,
        }
        if err := client.Send(idRequest); err != nil {
            return nil, err
        }
    }

    // 发送 fileName
    {
        fileNameRequest := &pbwechatypuppet.MessageSendFileStreamRequest{
            FileBoxChunk: &pbwechatypuppet.FileBoxChunk{
                Name: &fileBox.Name,
            },
        }
        if err := client.Send(fileNameRequest); err != nil {
            return nil, err
        }
    }
    return &MessageSendFile{
        client:  client,
        fileBox: fileBox,
    }, nil
}

// DownloadFile 把 grpc download 流包装到 io.Reader 接口
type DownloadFile struct {
    client pbwechaty.Puppet_DownloadClient
    buffer bytes.Buffer
    done   bool
}

// Read 把 grpc download 流包装到 io.Reader 接口
func (m *DownloadFile) Read(p []byte) (n int, err error) {
    if m.done {
        return m.buffer.Read(p)
    }

    for {
        if m.buffer.Len() >= len(p) {
            break
        }
        recv, err := m.client.Recv()
        if err == io.EOF {
            m.done = true
            break
        }
        if err != nil {
            return 0, err
        }
        _, err = m.buffer.Write(recv.Chunk)
        if err != nil {
            return 0, err
        }
    }
    return m.buffer.Read(p)
}

// NewDownloadFile 把 grpc download 流包装到 io.Reader 接口
func NewDownloadFile(client pbwechaty.Puppet_DownloadClient) *DownloadFile {
    return &DownloadFile{
        client: client,
        buffer: bytes.Buffer{},
        done:   false,
    }
}

/**
 *  for testing propose, use 20KB as the threshold
 *  after stable we should use a value between 64KB to 256KB as the threshold
 */
const passThroughThresholdBytes = 20 * 1024 //nolint:unused, deadcode, varcheck  // TODO 未来会被用到

/**
 * 1. Green:
 *  Can be serialized directly
 */
var greenFileBoxTypes = helper.ArrayInt{
    filebox.TypeUrl,
    filebox.TypeUuid,
    filebox.TypeQRCode,
}

/**
 * 2. Yellow:
 *  Can be serialized directly, if the size is less than a threshold
 *  if it's bigger than the threshold,
 *  then it should be convert to a UUID file box before send out
 */
var yellowFileBoxTypes = helper.ArrayInt{
    filebox.TypeBase64,
}

func serializeFileBox(box *filebox.FileBox) (*filebox.FileBox, error) {
    if canPassthrough(box) {
        return box, nil
    }
    reader, err := box.ToReader()
    if err != nil {
        return nil, err
    }
    uuid, err := filebox.FromStream(reader).ToUuid()
    if err != nil {
        return nil, err
    }
    return filebox.FromUuid(uuid, filebox.WithName(box.Name)), nil
}

func canPassthrough(box *filebox.FileBox) bool {
    if greenFileBoxTypes.InArray(int(box.Type())) {
        return true
    }

    if !yellowFileBoxTypes.InArray(int(box.Type())) {
        return false
    }

    // checksize
    return true
}