github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/plugins/alexa/server.go (about) 1 // This file is part of the Smart Home 2 // Program complex distribution https://github.com/e154/smart-home 3 // Copyright (C) 2016-2023, Filippov Alex 4 // 5 // This library is free software: you can redistribute it and/or 6 // modify it under the terms of the GNU Lesser General Public 7 // License as published by the Free Software Foundation; either 8 // version 3 of the License, or (at your option) any later version. 9 // 10 // This library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 // Library General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public 16 // License along with this library. If not, see 17 // <https://www.gnu.org/licenses/>. 18 19 package alexa 20 21 import ( 22 "context" 23 "net/http" 24 "os" 25 "sync" 26 27 "github.com/gin-gonic/gin" 28 "go.uber.org/atomic" 29 30 "github.com/e154/smart-home/adaptors" 31 "github.com/e154/smart-home/common/apperr" 32 m "github.com/e154/smart-home/models" 33 "github.com/e154/smart-home/system/bus" 34 "github.com/e154/smart-home/system/scripts" 35 ) 36 37 // Server ... 38 type Server struct { 39 engine *gin.Engine 40 isStarted *atomic.Bool 41 addressPort *string 42 server *http.Server 43 skillLock *sync.Mutex 44 skills map[string]*Skill 45 adaptors *adaptors.Adaptors 46 config Config 47 scriptService scripts.ScriptService 48 //gate *client.GateClient 49 eventBus bus.Bus 50 } 51 52 // NewServer ... 53 func NewServer(adaptors *adaptors.Adaptors, 54 config Config, 55 scriptService scripts.ScriptService, 56 //gateClient *gate_client.GateClient, 57 eventBus bus.Bus) *Server { 58 return &Server{ 59 isStarted: atomic.NewBool(false), 60 adaptors: adaptors, 61 skillLock: &sync.Mutex{}, 62 skills: make(map[string]*Skill), 63 config: config, 64 scriptService: scriptService, 65 //gate: gateClient, 66 eventBus: eventBus, 67 } 68 } 69 70 // Start ... 71 func (s *Server) Start() { 72 73 if !s.isStarted.CompareAndSwap(false, true) { 74 return 75 } 76 77 s.init() 78 79 logger := NewLogger() 80 81 gin.DisableConsoleColor() 82 gin.DefaultWriter = logger 83 gin.DefaultErrorWriter = logger 84 gin.SetMode(gin.ReleaseMode) 85 86 s.engine = gin.New() 87 s.engine.POST("/*any", s.Auth, s.handlerFunc) 88 89 s.server = &http.Server{ 90 Addr: s.config.String(), 91 Handler: s.engine, 92 } 93 94 go func() { 95 if err := s.server.ListenAndServe(); err != nil && err != http.ErrServerClosed { 96 log.Fatalf("listen: %s", err.Error()) 97 } 98 }() 99 100 //todo fix 101 //s.gate.SetAlexaApiEngine(s.engine) 102 103 log.Infof("Serving server at %s", s.config.String()) 104 } 105 106 func (s *Server) init() { 107 108 list, err := s.adaptors.AlexaSkill.ListEnabled(context.Background(), 999, 0) 109 if err != nil { 110 log.Error(err.Error()) 111 return 112 } 113 114 for _, skill := range list { 115 s.AddSkill(skill) 116 } 117 } 118 119 // Stop ... 120 func (s *Server) Stop() { 121 if !s.isStarted.CompareAndSwap(true, false) { 122 return 123 } 124 125 //todo fix 126 //s.gate.SetAlexaApiEngine(nil) 127 128 if s.server != nil { 129 _ = s.server.Close() 130 } 131 } 132 133 func (s *Server) handlerFunc(ctx *gin.Context) { 134 135 log.Info("new request") 136 137 req := &Request{} 138 if err := ctx.ShouldBindJSON(req); err != nil { 139 log.Error(err.Error()) 140 _ = ctx.AbortWithError(400, err) 141 return 142 } 143 144 resp := NewResponse() 145 146 switch req.GetRequestType() { 147 case "LaunchRequest": 148 s.OnLaunchHandler(ctx, req, resp) 149 case "IntentRequest": 150 s.OnIntentHandle(ctx, req, resp) 151 case "SessionEndedRequest": 152 s.OnSessionEndedHandler(ctx, req, resp) 153 case "AudioPlayer": 154 s.OnAudioPlayerHandler(ctx, req, resp) 155 default: 156 http.Error(ctx.Writer, "Invalid request.", http.StatusBadRequest) 157 } 158 159 ctx.Writer.Header().Set("Content-Type", "application/json;charset=UTF-8") 160 161 b, _ := resp.String() 162 _, _ = ctx.Writer.Write(b) 163 } 164 165 // OnLaunchHandler ... 166 func (s *Server) OnLaunchHandler(ctx *gin.Context, req *Request, resp *Response) { 167 s.skillLock.Lock() 168 defer s.skillLock.Unlock() 169 170 if skill, ok := s.skills[req.Context.System.Application.ApplicationID]; ok { 171 skill.OnLaunch(ctx, req, resp) 172 } 173 } 174 175 // OnIntentHandle ... 176 func (s *Server) OnIntentHandle(ctx *gin.Context, req *Request, resp *Response) { 177 s.skillLock.Lock() 178 defer s.skillLock.Unlock() 179 180 if skill, ok := s.skills[req.Context.System.Application.ApplicationID]; ok { 181 skill.OnIntent(ctx, req, resp) 182 } 183 } 184 185 // OnSessionEndedHandler ... 186 func (s *Server) OnSessionEndedHandler(ctx *gin.Context, req *Request, resp *Response) { 187 s.skillLock.Lock() 188 defer s.skillLock.Unlock() 189 190 if skill, ok := s.skills[req.Context.System.Application.ApplicationID]; ok { 191 skill.OnSessionEnded(ctx, req, resp) 192 } 193 } 194 195 // OnAudioPlayerHandler ... 196 func (s *Server) OnAudioPlayerHandler(ctx *gin.Context, req *Request, resp *Response) { 197 s.skillLock.Lock() 198 defer s.skillLock.Unlock() 199 200 for _, skill := range s.skills { 201 if skill.GetAppID() != req.Context.System.Application.ApplicationID { 202 continue 203 } 204 //todo check 205 //if skill.OnAudioPlayerState != nil { 206 // skill.OnAudioPlayerState(ctx, req, resp) 207 //} 208 } 209 } 210 211 // Auth ... 212 func (s Server) Auth(ctx *gin.Context) { 213 214 if os.Getenv("DEV") == "true" { 215 return 216 } 217 218 if !IsValidAlexaRequest(ctx.Writer, ctx.Request) { 219 _ = ctx.AbortWithError(401, apperr.ErrBadRequestParams) 220 return 221 } 222 } 223 224 // AddSkill ... 225 func (s *Server) AddSkill(skill *m.AlexaSkill) { 226 s.skillLock.Lock() 227 defer s.skillLock.Unlock() 228 229 if _, ok := s.skills[skill.SkillId]; !ok { 230 s.skills[skill.SkillId] = NewSkill(skill, s.adaptors, s.scriptService, s.eventBus) 231 } 232 } 233 234 // UpdateSkill ... 235 func (s *Server) UpdateSkill(skill *m.AlexaSkill) { 236 s.skillLock.Lock() 237 defer s.skillLock.Unlock() 238 239 s.skills[skill.SkillId] = NewSkill(skill, s.adaptors, s.scriptService, s.eventBus) 240 } 241 242 // DeleteSkill ... 243 func (s *Server) DeleteSkill(skill *m.AlexaSkill) { 244 s.skillLock.Lock() 245 defer s.skillLock.Unlock() 246 247 if _, ok := s.skills[skill.SkillId]; !ok { 248 delete(s.skills, skill.SkillId) 249 } 250 }