func (m *MsgParam) ParseFormData(c *Core, ctx *gin.Context) (*model.Message, error) {
	var msg *model.Message = nil
	if form, err := ctx.MultipartForm(); err == nil {
		ts := form.Value["text"]
		if len(ts) > 0 {
			m.Text = ts[0]
		}
		if m.Token == nil {
			tks := form.Value["token"]
			if len(tks) > 0 {
				m.Token, _ = c.parseToken(tks[0])
			}
		}
		m.Title = tryFormValue(form, "title", m.Title)
		m.Link = tryFormValue(form, "link", m.Link)
		m.CopyText = tryFormValue(form, "copy", m.CopyText)
		m.AutoCopy = tryFormValue(form, "autocopy", m.AutoCopy)
		m.Filename = tryFormValue(form, "filename", m.Filename)
		m.Sound = tryFormValue(form, "sound", m.Sound)
		m.Actions = tryFormValues(form, "action", m.Actions)
		m.parsePriorityFromForm(form)
		m.InterruptionLevel = tryFormValue(form, "interruption-level", m.InterruptionLevel)
		m.TimeContent.Code = tryFormValue(form, "timeline-code", m.TimeContent.Code)
		if len(m.TimeContent.Code) > 0 {
			m.TimeContent.Timestamp = tryFormTimestamp(form, "timeline-timestamp", m.TimeContent.Timestamp)
			m.TimeContent.Items = tryFormMap(form, "timeline-items", m.TimeContent.Items)
		}
		if m.Token != nil && c.logic.CanFileStore() {
			if data, _, err := readFileFromForm(form, "image"); err == nil {
				msg, err = c.saveUploadImage(ctx, m.Token, data)
				if err != nil {
					return nil, err
				}
			}
			if data, fname, err := readFileFromForm(form, "audio"); err == nil {
				if len(m.Filename) > 0 {
					fname = m.Filename
				}
				msg, err = c.saveUploadAudio(ctx, m.Token, fileBaseName(fname), m.Title, data)
				if err != nil {
					return nil, err
				}
			}
			if data, fname, err := readFileFromForm(form, "file"); err == nil {
				msg, err = c.saveUploadFile(ctx, m.Token, data, fileBaseName(fname), m.Text, m.Actions)
				if err != nil {
					return nil, err
				}
			}
		}
	}
	return msg, nil
}
ParseFormData process multipart/form-data
package core
import (
	"io/ioutil"
	"mime/multipart"
	"strconv"
	"strings"
	"time"
	"github.com/chanify/chanify/model"
	"github.com/gin-gonic/gin"
)
type TimeContent struct {
	Code      string
	Timestamp *time.Time
	Items     []*model.MsgTimeItem
}
type MsgParam struct {
	Token             *model.Token
	Text              string
	Link              string
	Title             string
	Sound             string
	AutoCopy          string
	CopyText          string
	Filename          string
	Priority          int
	InterruptionLevel string
	Actions           []string
	TimeContent       TimeContent
}
func (m *MsgParam) ParsePlainText(ctx *gin.Context) {
	defer ctx.Request.Body.Close()
	if d, err := ioutil.ReadAll(ctx.Request.Body); err == nil {
		m.Text = string(d)
	}
}
func (m *MsgParam) ParseJSON(c *Core, ctx *gin.Context) {
	defer ctx.Request.Body.Close()
	var params struct {
		Token             string     `json:"token,omitempty"`
		Title             string     `json:"title,omitempty"`
		Text              string     `json:"text,omitempty"`
		Copy              string     `json:"copy,omitempty"`
		AutoCopy          JSONString `json:"autocopy,omitempty"`
		Link              string     `json:"link,omitempty"`
		Sound             JSONString `json:"sound,omitempty"`
		Priority          int        `json:"priority,omitempty"`
		InterruptionLevel string     `json:"interruption-level,omitempty"`
		Actions           []string   `json:"actions,omitempty"`
		Timeline          struct {
			Code     string                 `json:"code"`
			Timstamp interface{}            `json:"timestamp,omitempty"`
			Items    map[string]interface{} `json:"items"`
		} `json:"timeline,omitempty"`
	}
	if err := ctx.BindJSON(&params); err == nil {
		if m.Token == nil && len(params.Token) > 0 {
			m.Token, _ = c.parseToken(params.Token)
		}
		m.Link = tryStringValue(m.Link, params.Link)
		m.Title = tryStringValue(m.Title, params.Title)
		if len(m.Sound) <= 0 && len(params.Sound) > 0 {
			m.Sound = string(params.Sound)
		}
		m.CopyText = tryStringValue(m.CopyText, params.Copy)
		if len(m.AutoCopy) <= 0 && len(params.AutoCopy) > 0 {
			m.AutoCopy = string(params.AutoCopy)
		}
		if len(m.Actions) <= 0 {
			m.Actions = params.Actions
		}
		if m.Priority <= 0 {
			m.Priority = params.Priority
		}
		if len(m.InterruptionLevel) <= 0 {
			m.InterruptionLevel = params.InterruptionLevel
		}
		if len(m.TimeContent.Code) <= 0 {
			m.TimeContent.Code = params.Timeline.Code
			m.TimeContent.Timestamp = parseTimestamp(params.Timeline.Timstamp)
			m.TimeContent.Items = parseTimeContentItems(params.Timeline.Items)
		}
		m.Text = params.Text
	}
}
func (m *MsgParam) ParseForm(c *Core, ctx *gin.Context) {
	m.Text = ctx.PostForm("text")
	if m.Token == nil {
		m.Token, _ = c.parseToken(ctx.PostForm("token"))
	}
	if len(m.Link) <= 0 {
		m.Link = ctx.PostForm("link")
	}
	if len(m.CopyText) <= 0 {
		m.CopyText = ctx.PostForm("copy")
	}
	if len(m.AutoCopy) <= 0 {
		m.AutoCopy = ctx.PostForm("autocopy")
	}
	if len(m.Sound) <= 0 {
		m.Sound = ctx.PostForm("sound")
	}
	if len(m.Actions) <= 0 {
		m.Actions = ctx.PostFormArray("action")
	}
	if m.Priority <= 0 {
		m.Priority = parsePriority(ctx.PostForm("priority"))
	}
	if len(m.InterruptionLevel) <= 0 {
		m.InterruptionLevel = ctx.PostForm("interruption-level")
	}
	if len(m.TimeContent.Code) <= 0 {
		m.TimeContent.Code = ctx.PostForm("timeline-code")
		m.TimeContent.Timestamp = parseTimestamp(ctx.PostForm("timeline-timestamp"))
		m.TimeContent.Items = parseTimeContentStringItems(ctx.PostFormMap("timeline-items"))
	}
}