WIP: iOS poll_request forwarder
This commit is contained in:
		
							parent
							
								
									4dabc56952
								
							
						
					
					
						commit
						6a43c1a126
					
				
					 5 changed files with 85 additions and 11 deletions
				
			
		| 
						 | 
					@ -41,6 +41,7 @@ var flagsServe = []cli.Flag{
 | 
				
			||||||
	altsrc.NewDurationFlag(&cli.DurationFlag{Name: "keepalive-interval", Aliases: []string{"keepalive_interval", "k"}, EnvVars: []string{"NTFY_KEEPALIVE_INTERVAL"}, Value: server.DefaultKeepaliveInterval, Usage: "interval of keepalive messages"}),
 | 
						altsrc.NewDurationFlag(&cli.DurationFlag{Name: "keepalive-interval", Aliases: []string{"keepalive_interval", "k"}, EnvVars: []string{"NTFY_KEEPALIVE_INTERVAL"}, Value: server.DefaultKeepaliveInterval, Usage: "interval of keepalive messages"}),
 | 
				
			||||||
	altsrc.NewDurationFlag(&cli.DurationFlag{Name: "manager-interval", Aliases: []string{"manager_interval", "m"}, EnvVars: []string{"NTFY_MANAGER_INTERVAL"}, Value: server.DefaultManagerInterval, Usage: "interval of for message pruning and stats printing"}),
 | 
						altsrc.NewDurationFlag(&cli.DurationFlag{Name: "manager-interval", Aliases: []string{"manager_interval", "m"}, EnvVars: []string{"NTFY_MANAGER_INTERVAL"}, Value: server.DefaultManagerInterval, Usage: "interval of for message pruning and stats printing"}),
 | 
				
			||||||
	altsrc.NewStringFlag(&cli.StringFlag{Name: "web-root", Aliases: []string{"web_root"}, EnvVars: []string{"NTFY_WEB_ROOT"}, Value: "app", Usage: "sets web root to landing page (home), web app (app) or disabled (disable)"}),
 | 
						altsrc.NewStringFlag(&cli.StringFlag{Name: "web-root", Aliases: []string{"web_root"}, EnvVars: []string{"NTFY_WEB_ROOT"}, Value: "app", Usage: "sets web root to landing page (home), web app (app) or disabled (disable)"}),
 | 
				
			||||||
 | 
						altsrc.NewStringFlag(&cli.StringFlag{Name: "forward-poll-url", Aliases: []string{"forward_poll_url"}, EnvVars: []string{"NTFY_FORWARD_POLL_URL"}, Value: "", Usage: ""}),
 | 
				
			||||||
	altsrc.NewStringFlag(&cli.StringFlag{Name: "smtp-sender-addr", Aliases: []string{"smtp_sender_addr"}, EnvVars: []string{"NTFY_SMTP_SENDER_ADDR"}, Usage: "SMTP server address (host:port) for outgoing emails"}),
 | 
						altsrc.NewStringFlag(&cli.StringFlag{Name: "smtp-sender-addr", Aliases: []string{"smtp_sender_addr"}, EnvVars: []string{"NTFY_SMTP_SENDER_ADDR"}, Usage: "SMTP server address (host:port) for outgoing emails"}),
 | 
				
			||||||
	altsrc.NewStringFlag(&cli.StringFlag{Name: "smtp-sender-user", Aliases: []string{"smtp_sender_user"}, EnvVars: []string{"NTFY_SMTP_SENDER_USER"}, Usage: "SMTP user (if e-mail sending is enabled)"}),
 | 
						altsrc.NewStringFlag(&cli.StringFlag{Name: "smtp-sender-user", Aliases: []string{"smtp_sender_user"}, EnvVars: []string{"NTFY_SMTP_SENDER_USER"}, Usage: "SMTP user (if e-mail sending is enabled)"}),
 | 
				
			||||||
	altsrc.NewStringFlag(&cli.StringFlag{Name: "smtp-sender-pass", Aliases: []string{"smtp_sender_pass"}, EnvVars: []string{"NTFY_SMTP_SENDER_PASS"}, Usage: "SMTP password (if e-mail sending is enabled)"}),
 | 
						altsrc.NewStringFlag(&cli.StringFlag{Name: "smtp-sender-pass", Aliases: []string{"smtp_sender_pass"}, EnvVars: []string{"NTFY_SMTP_SENDER_PASS"}, Usage: "SMTP password (if e-mail sending is enabled)"}),
 | 
				
			||||||
| 
						 | 
					@ -102,6 +103,7 @@ func execServe(c *cli.Context) error {
 | 
				
			||||||
	keepaliveInterval := c.Duration("keepalive-interval")
 | 
						keepaliveInterval := c.Duration("keepalive-interval")
 | 
				
			||||||
	managerInterval := c.Duration("manager-interval")
 | 
						managerInterval := c.Duration("manager-interval")
 | 
				
			||||||
	webRoot := c.String("web-root")
 | 
						webRoot := c.String("web-root")
 | 
				
			||||||
 | 
						forwardPollURL := c.String("forward-poll-url")
 | 
				
			||||||
	smtpSenderAddr := c.String("smtp-sender-addr")
 | 
						smtpSenderAddr := c.String("smtp-sender-addr")
 | 
				
			||||||
	smtpSenderUser := c.String("smtp-sender-user")
 | 
						smtpSenderUser := c.String("smtp-sender-user")
 | 
				
			||||||
	smtpSenderPass := c.String("smtp-sender-pass")
 | 
						smtpSenderPass := c.String("smtp-sender-pass")
 | 
				
			||||||
| 
						 | 
					@ -147,6 +149,8 @@ func execServe(c *cli.Context) error {
 | 
				
			||||||
		return errors.New("if set, auth-default-access must start set to 'read-write', 'read-only', 'write-only' or 'deny-all'")
 | 
							return errors.New("if set, auth-default-access must start set to 'read-write', 'read-only', 'write-only' or 'deny-all'")
 | 
				
			||||||
	} else if !util.InStringList([]string{"app", "home", "disable"}, webRoot) {
 | 
						} else if !util.InStringList([]string{"app", "home", "disable"}, webRoot) {
 | 
				
			||||||
		return errors.New("if set, web-root must be 'home' or 'app'")
 | 
							return errors.New("if set, web-root must be 'home' or 'app'")
 | 
				
			||||||
 | 
						} else if forwardPollURL != "" && !strings.HasPrefix(forwardPollURL, "http://") && !strings.HasPrefix(forwardPollURL, "https://") {
 | 
				
			||||||
 | 
							return errors.New("if set, forward-poll-url must start with http:// or https://")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	webRootIsApp := webRoot == "app"
 | 
						webRootIsApp := webRoot == "app"
 | 
				
			||||||
| 
						 | 
					@ -215,6 +219,7 @@ func execServe(c *cli.Context) error {
 | 
				
			||||||
	conf.KeepaliveInterval = keepaliveInterval
 | 
						conf.KeepaliveInterval = keepaliveInterval
 | 
				
			||||||
	conf.ManagerInterval = managerInterval
 | 
						conf.ManagerInterval = managerInterval
 | 
				
			||||||
	conf.WebRootIsApp = webRootIsApp
 | 
						conf.WebRootIsApp = webRootIsApp
 | 
				
			||||||
 | 
						conf.ForwardPollURL = forwardPollURL
 | 
				
			||||||
	conf.SMTPSenderAddr = smtpSenderAddr
 | 
						conf.SMTPSenderAddr = smtpSenderAddr
 | 
				
			||||||
	conf.SMTPSenderUser = smtpSenderUser
 | 
						conf.SMTPSenderUser = smtpSenderUser
 | 
				
			||||||
	conf.SMTPSenderPass = smtpSenderPass
 | 
						conf.SMTPSenderPass = smtpSenderPass
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -69,6 +69,7 @@ type Config struct {
 | 
				
			||||||
	AtSenderInterval                     time.Duration
 | 
						AtSenderInterval                     time.Duration
 | 
				
			||||||
	FirebaseKeepaliveInterval            time.Duration
 | 
						FirebaseKeepaliveInterval            time.Duration
 | 
				
			||||||
	FirebasePollInterval                 time.Duration
 | 
						FirebasePollInterval                 time.Duration
 | 
				
			||||||
 | 
						ForwardPollURL                       string
 | 
				
			||||||
	SMTPSenderAddr                       string
 | 
						SMTPSenderAddr                       string
 | 
				
			||||||
	SMTPSenderUser                       string
 | 
						SMTPSenderUser                       string
 | 
				
			||||||
	SMTPSenderPass                       string
 | 
						SMTPSenderPass                       string
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@ package server
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
 | 
						"crypto/sha256"
 | 
				
			||||||
	"embed"
 | 
						"embed"
 | 
				
			||||||
	"encoding/base64"
 | 
						"encoding/base64"
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
| 
						 | 
					@ -93,6 +94,7 @@ const (
 | 
				
			||||||
	firebaseControlTopic     = "~control"                // See Android if changed
 | 
						firebaseControlTopic     = "~control"                // See Android if changed
 | 
				
			||||||
	firebasePollTopic        = "~poll"                   // See iOS if changed
 | 
						firebasePollTopic        = "~poll"                   // See iOS if changed
 | 
				
			||||||
	emptyMessageBody         = "triggered"               // Used if message body is empty
 | 
						emptyMessageBody         = "triggered"               // Used if message body is empty
 | 
				
			||||||
 | 
						newMessageBody           = "New message"             // Used in poll requests as generic message
 | 
				
			||||||
	defaultAttachmentMessage = "You received a file: %s" // Used if message body is empty, and there is an attachment
 | 
						defaultAttachmentMessage = "You received a file: %s" // Used if message body is empty, and there is an attachment
 | 
				
			||||||
	encodingBase64           = "base64"
 | 
						encodingBase64           = "base64"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -422,6 +424,9 @@ func (s *Server) handlePublish(w http.ResponseWriter, r *http.Request, v *visito
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if m.PollID != "" {
 | 
				
			||||||
 | 
							m = newPollRequestMessage(t.ID, m.PollID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if err := s.handlePublishBody(r, v, m, body, unifiedpush); err != nil {
 | 
						if err := s.handlePublishBody(r, v, m, body, unifiedpush); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -448,6 +453,28 @@ func (s *Server) handlePublish(w http.ResponseWriter, r *http.Request, v *visito
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}()
 | 
							}()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if s.config.ForwardPollURL != "" {
 | 
				
			||||||
 | 
							go func() {
 | 
				
			||||||
 | 
								topicURL := fmt.Sprintf("%s/%s", s.config.BaseURL, m.Topic)
 | 
				
			||||||
 | 
								topicHash := fmt.Sprintf("%x", sha256.Sum256([]byte(topicURL)))
 | 
				
			||||||
 | 
								forwardURL := fmt.Sprintf("%s/%s", s.config.ForwardPollURL, topicHash)
 | 
				
			||||||
 | 
								log.Printf("forwarding: topicURL %s, to upstream url %s", topicURL, forwardURL)
 | 
				
			||||||
 | 
								req, err := http.NewRequest("POST", forwardURL, strings.NewReader(""))
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									log.Printf("[%s] FWD - Unable to forward poll request: %v", v.ip, err.Error())
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								req.Header.Set("X-Poll-ID", m.ID)
 | 
				
			||||||
 | 
								response, err := http.DefaultClient.Do(req)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									log.Printf("[%s] FWD - Unable to forward poll request: %v", v.ip, err.Error())
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								} else if response.StatusCode != http.StatusOK {
 | 
				
			||||||
 | 
									log.Printf("[%s] FWD - Unable to forward poll request, unexpected status: %d", v.ip, response.StatusCode)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if cache {
 | 
						if cache {
 | 
				
			||||||
		if err := s.messageCache.AddMessage(m); err != nil {
 | 
							if err := s.messageCache.AddMessage(m); err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
| 
						 | 
					@ -549,6 +576,12 @@ func (s *Server) parsePublishParams(r *http.Request, v *visitor, m *message) (ca
 | 
				
			||||||
		firebase = false
 | 
							firebase = false
 | 
				
			||||||
		unifiedpush = true
 | 
							unifiedpush = true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						m.PollID = readParam(r, "x-poll-id", "poll-id", "poll")
 | 
				
			||||||
 | 
						if m.PollID != "" {
 | 
				
			||||||
 | 
							unifiedpush = false
 | 
				
			||||||
 | 
							cache = false
 | 
				
			||||||
 | 
							email = ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return cache, firebase, email, unifiedpush, nil
 | 
						return cache, firebase, email, unifiedpush, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -565,7 +598,9 @@ func (s *Server) parsePublishParams(r *http.Request, v *visitor, m *message) (ca
 | 
				
			||||||
// 5. curl -T file.txt ntfy.sh/mytopic
 | 
					// 5. curl -T file.txt ntfy.sh/mytopic
 | 
				
			||||||
//    If file.txt is > message limit, treat it as an attachment
 | 
					//    If file.txt is > message limit, treat it as an attachment
 | 
				
			||||||
func (s *Server) handlePublishBody(r *http.Request, v *visitor, m *message, body *util.PeekedReadCloser, unifiedpush bool) error {
 | 
					func (s *Server) handlePublishBody(r *http.Request, v *visitor, m *message, body *util.PeekedReadCloser, unifiedpush bool) error {
 | 
				
			||||||
	if unifiedpush {
 | 
						if m.Event == pollRequestEvent {
 | 
				
			||||||
 | 
							return nil // Ignore body
 | 
				
			||||||
 | 
						} else if unifiedpush {
 | 
				
			||||||
		return s.handleBodyAsMessageAutoDetect(m, body) // Case 1
 | 
							return s.handleBodyAsMessageAutoDetect(m, body) // Case 1
 | 
				
			||||||
	} else if m.Attachment != nil && m.Attachment.URL != "" {
 | 
						} else if m.Attachment != nil && m.Attachment.URL != "" {
 | 
				
			||||||
		return s.handleBodyAsTextMessage(m, body) // Case 2
 | 
							return s.handleBodyAsTextMessage(m, body) // Case 2
 | 
				
			||||||
| 
						 | 
					@ -710,6 +745,7 @@ func (s *Server) handleSubscribeHTTP(w http.ResponseWriter, r *http.Request, v *
 | 
				
			||||||
	w.Header().Set("Access-Control-Allow-Origin", "*")            // CORS, allow cross-origin requests
 | 
						w.Header().Set("Access-Control-Allow-Origin", "*")            // CORS, allow cross-origin requests
 | 
				
			||||||
	w.Header().Set("Content-Type", contentType+"; charset=utf-8") // Android/Volley client needs charset!
 | 
						w.Header().Set("Content-Type", contentType+"; charset=utf-8") // Android/Volley client needs charset!
 | 
				
			||||||
	if poll {
 | 
						if poll {
 | 
				
			||||||
 | 
							log.Printf("polling %#v", r.URL)
 | 
				
			||||||
		return s.sendOldMessages(topics, since, scheduled, sub)
 | 
							return s.sendOldMessages(topics, since, scheduled, sub)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	subscriberIDs := make([]int, 0)
 | 
						subscriberIDs := make([]int, 0)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,7 @@ import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	firebase "firebase.google.com/go"
 | 
						firebase "firebase.google.com/go"
 | 
				
			||||||
| 
						 | 
					@ -64,6 +65,7 @@ func createFirebaseSubscriber(credentialsFile string, auther auth.Auther) (subsc
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							log.Printf("Sending %#v %#v", m, fbm)
 | 
				
			||||||
		_, err = msg.Send(context.Background(), fbm)
 | 
							_, err = msg.Send(context.Background(), fbm)
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}, nil
 | 
						}, nil
 | 
				
			||||||
| 
						 | 
					@ -98,6 +100,31 @@ func toFirebaseMessage(m *message, auther auth.Auther) (*messaging.Message, erro
 | 
				
			||||||
				CustomData: apnsData,
 | 
									CustomData: apnsData,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						case pollRequestEvent:
 | 
				
			||||||
 | 
							data = map[string]string{
 | 
				
			||||||
 | 
								"id":      m.ID,
 | 
				
			||||||
 | 
								"time":    fmt.Sprintf("%d", m.Time),
 | 
				
			||||||
 | 
								"event":   m.Event,
 | 
				
			||||||
 | 
								"topic":   m.Topic,
 | 
				
			||||||
 | 
								"message": m.Message,
 | 
				
			||||||
 | 
								"poll_id": m.PollID,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							apnsData := make(map[string]interface{})
 | 
				
			||||||
 | 
							for k, v := range data {
 | 
				
			||||||
 | 
								apnsData[k] = v
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							apnsConfig = &messaging.APNSConfig{
 | 
				
			||||||
 | 
								Payload: &messaging.APNSPayload{
 | 
				
			||||||
 | 
									CustomData: apnsData,
 | 
				
			||||||
 | 
									Aps: &messaging.Aps{
 | 
				
			||||||
 | 
										MutableContent: true,
 | 
				
			||||||
 | 
										Alert: &messaging.ApsAlert{
 | 
				
			||||||
 | 
											Title: m.Title,
 | 
				
			||||||
 | 
											Body:  maybeTruncateAPNSBodyMessage(m.Message),
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	case messageEvent:
 | 
						case messageEvent:
 | 
				
			||||||
		allowForward := true
 | 
							allowForward := true
 | 
				
			||||||
		if auther != nil {
 | 
							if auther != nil {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,13 +24,14 @@ type message struct {
 | 
				
			||||||
	Time       int64       `json:"time"`  // Unix time in seconds
 | 
						Time       int64       `json:"time"`  // Unix time in seconds
 | 
				
			||||||
	Event      string      `json:"event"` // One of the above
 | 
						Event      string      `json:"event"` // One of the above
 | 
				
			||||||
	Topic      string      `json:"topic"`
 | 
						Topic      string      `json:"topic"`
 | 
				
			||||||
 | 
						Title      string      `json:"title,omitempty"`
 | 
				
			||||||
 | 
						Message    string      `json:"message,omitempty"`
 | 
				
			||||||
	Priority   int         `json:"priority,omitempty"`
 | 
						Priority   int         `json:"priority,omitempty"`
 | 
				
			||||||
	Tags       []string    `json:"tags,omitempty"`
 | 
						Tags       []string    `json:"tags,omitempty"`
 | 
				
			||||||
	Click      string      `json:"click,omitempty"`
 | 
						Click      string      `json:"click,omitempty"`
 | 
				
			||||||
	Actions    []*action   `json:"actions,omitempty"`
 | 
						Actions    []*action   `json:"actions,omitempty"`
 | 
				
			||||||
	Attachment *attachment `json:"attachment,omitempty"`
 | 
						Attachment *attachment `json:"attachment,omitempty"`
 | 
				
			||||||
	Title      string      `json:"title,omitempty"`
 | 
						PollID     string      `json:"poll_id,omitempty"`
 | 
				
			||||||
	Message    string      `json:"message,omitempty"`
 | 
					 | 
				
			||||||
	Encoding   string      `json:"encoding,omitempty"` // empty for raw UTF-8, or "base64" for encoded bytes
 | 
						Encoding   string      `json:"encoding,omitempty"` // empty for raw UTF-8, or "base64" for encoded bytes
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -84,14 +85,11 @@ type messageEncoder func(msg *message) (string, error)
 | 
				
			||||||
// newMessage creates a new message with the current timestamp
 | 
					// newMessage creates a new message with the current timestamp
 | 
				
			||||||
func newMessage(event, topic, msg string) *message {
 | 
					func newMessage(event, topic, msg string) *message {
 | 
				
			||||||
	return &message{
 | 
						return &message{
 | 
				
			||||||
		ID:       util.RandomString(messageIDLength),
 | 
							ID:      util.RandomString(messageIDLength),
 | 
				
			||||||
		Time:     time.Now().Unix(),
 | 
							Time:    time.Now().Unix(),
 | 
				
			||||||
		Event:    event,
 | 
							Event:   event,
 | 
				
			||||||
		Topic:    topic,
 | 
							Topic:   topic,
 | 
				
			||||||
		Priority: 0,
 | 
							Message: msg,
 | 
				
			||||||
		Tags:     nil,
 | 
					 | 
				
			||||||
		Title:    "",
 | 
					 | 
				
			||||||
		Message:  msg,
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -110,6 +108,13 @@ func newDefaultMessage(topic, msg string) *message {
 | 
				
			||||||
	return newMessage(messageEvent, topic, msg)
 | 
						return newMessage(messageEvent, topic, msg)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// newPollRequestMessage is a convenience method to create a poll request message
 | 
				
			||||||
 | 
					func newPollRequestMessage(topic, pollID string) *message {
 | 
				
			||||||
 | 
						m := newMessage(pollRequestEvent, topic, newMessageBody)
 | 
				
			||||||
 | 
						m.PollID = pollID
 | 
				
			||||||
 | 
						return m
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func validMessageID(s string) bool {
 | 
					func validMessageID(s string) bool {
 | 
				
			||||||
	return util.ValidRandomString(s, messageIDLength)
 | 
						return util.ValidRandomString(s, messageIDLength)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue