1st
This commit is contained in:
875
daemon/onexbet.go
Executable file
875
daemon/onexbet.go
Executable file
@@ -0,0 +1,875 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gordenko.dev/dima/flashscore"
|
||||
"gordenko.dev/dima/onexbet"
|
||||
"gordenko.dev/dima/tipper/model"
|
||||
"gordenko.dev/dima/web/api"
|
||||
"gordenko.dev/dima/ws"
|
||||
)
|
||||
|
||||
func (s *Daemon) tryLinkToLiveMatch(sportID int, liveMatch onexbet.LiveMatch) {
|
||||
matchName := fmt.Sprintf("%s - %s", liveMatch.Home, liveMatch.Away)
|
||||
|
||||
s.logger.Printf("try link: %s\n", matchName)
|
||||
// Если уже распознан - пропускаем
|
||||
|
||||
// На всякий пожарный проверка
|
||||
//_, ok := s.onexbetMatchToTeamMatch[liveMatch.MatchID]
|
||||
//if ok {
|
||||
// s.logger.Printf("try link: %s\n", matchName)
|
||||
// return
|
||||
//}
|
||||
|
||||
// Если этот матч уже проверяли - игнорируем
|
||||
s.mutex.Lock()
|
||||
|
||||
unrecognizedMatches, ok := s.unrecognizedOnexbetMatches[sportID]
|
||||
if !ok {
|
||||
s.logger.Printf("unrecognized matches for the sport %d not found\n", sportID)
|
||||
return
|
||||
}
|
||||
|
||||
if unrecognizedMatches[liveMatch.MatchID] {
|
||||
//s.logger.Printf("Match (%s) is unrecognized. Don't repeat identification\n", matchName)
|
||||
s.mutex.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
s.mutex.Unlock()
|
||||
|
||||
homeTeamCandidates, err := s.fsmodel.ListTeamCandidates(sportID, liveMatch.Home)
|
||||
if err != nil {
|
||||
s.logger.Printf("fsmodel.ListFootballTeamCandidates: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
//s.logger.Printf("homeTeamCandidates (for %s): %q", liveMatch.Home, homeTeamCandidates)
|
||||
|
||||
awayTeamCandidates, err := s.fsmodel.ListTeamCandidates(sportID, liveMatch.Away)
|
||||
if err != nil {
|
||||
s.logger.Printf("fsmodel.ListFootballTeamCandidates: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
//s.logger.Printf("awayTeamCandidates (for %s): %q", liveMatch.Away, awayTeamCandidates)
|
||||
//
|
||||
|
||||
teamMatches, ok := s.teamMatches[sportID]
|
||||
if !ok {
|
||||
err = fmt.Errorf("Not found matches for the sportID %d", sportID)
|
||||
return
|
||||
}
|
||||
|
||||
for _, match := range teamMatches {
|
||||
var isTeamsSwapped bool
|
||||
|
||||
isIdentified := s.tryIdentifyMatch(match.Home.TeamID, match.Away.TeamID,
|
||||
homeTeamCandidates, awayTeamCandidates)
|
||||
|
||||
if !isIdentified {
|
||||
// Пробуем опознать если home и away поменять местами
|
||||
isIdentified = s.tryIdentifyMatch(match.Home.TeamID, match.Away.TeamID,
|
||||
awayTeamCandidates, homeTeamCandidates)
|
||||
|
||||
isTeamsSwapped = true
|
||||
}
|
||||
|
||||
if isIdentified {
|
||||
|
||||
if !match.Home.WasLinked {
|
||||
err = s.fsmodel.SetTeamWasLinked(match.Home.TeamID)
|
||||
if err != nil {
|
||||
s.logger.Printf("fsmodel.SetTeamWasLinked: %s; teamID=%s\n",
|
||||
err, match.Home.TeamID)
|
||||
}
|
||||
s.mutex.Lock()
|
||||
match.Home.WasLinked = true
|
||||
s.mutex.Unlock()
|
||||
}
|
||||
|
||||
if !match.Away.WasLinked {
|
||||
err = s.fsmodel.SetTeamWasLinked(match.Away.TeamID)
|
||||
if err != nil {
|
||||
s.logger.Printf("fsmodel.SetTeamWasLinked: %s; teamID=%s\n",
|
||||
err, match.Away.TeamID)
|
||||
}
|
||||
s.mutex.Lock()
|
||||
match.Away.WasLinked = true
|
||||
s.mutex.Unlock()
|
||||
}
|
||||
|
||||
s.mutex.Lock()
|
||||
match.IsLinkedToOnexbet = true
|
||||
match.OnexbetMatchID = liveMatch.MatchID
|
||||
match.OnexbetStatsMatchID = liveMatch.StatsMatchID
|
||||
match.Score = liveMatch.Score
|
||||
match.ScoreByPeriods = liveMatch.ScoreByPeriods
|
||||
match.CurrentPeriod = liveMatch.CurrentPeriod
|
||||
match.IsTeamsSwapped = isTeamsSwapped
|
||||
|
||||
s.onexbetMatchToTeamMatch[liveMatch.MatchID] = match
|
||||
s.identifiedMatches[liveMatch.MatchID] = true
|
||||
|
||||
s.logger.Printf("Identified: %s; %s; %s\n", matchName, liveMatch.MatchID, match.MatchID)
|
||||
|
||||
// Матч опознан и перешел в inplay
|
||||
s.publisher.Publish(
|
||||
getChannel(match.SportID),
|
||||
WSMessageInplayStatusChanged,
|
||||
InplayStatusMessage{
|
||||
MatchID: match.MatchID,
|
||||
IsInplay: match.IsInplay,
|
||||
IsLinkedToOnexbet: match.IsLinkedToOnexbet,
|
||||
},
|
||||
)
|
||||
s.mutex.Unlock()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
s.mutex.Lock()
|
||||
// Кешируем негативный результат, чтобы не искать в базе ежеминутно
|
||||
// Если пользователь распознает новые матчи, - кэш будет очищен
|
||||
unrecognizedMatches, ok = s.unrecognizedOnexbetMatches[sportID]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
unrecognizedMatches[liveMatch.MatchID] = true
|
||||
s.mutex.Unlock()
|
||||
|
||||
s.logger.Printf("Match (%s; %s) is not identified. Added to unrecognized\n", matchName, liveMatch.MatchID)
|
||||
}
|
||||
|
||||
func (s *Daemon) tryIdentifyMatch(homeTeamID, awayTeamID string, homeTeamCandidates, awayTeamCandidates []string) (_ bool) {
|
||||
for _, candidateHomeTeamID := range homeTeamCandidates {
|
||||
if homeTeamID == candidateHomeTeamID {
|
||||
// Если нашли совпадение для home - ищем совпадение для away
|
||||
for _, candidateAwayTeamID := range awayTeamCandidates {
|
||||
if awayTeamID == candidateAwayTeamID {
|
||||
// Mатч идентифицирован
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Daemon) onOnexbetWsClientConnected(conn *ws.Conn) {
|
||||
s.logger.Printf("Onexbet Ws Client Connected\n")
|
||||
}
|
||||
|
||||
/*
|
||||
func (s *Daemon) onOnexbetMatchFinished(conn *ws.Conn, in onexbet.WatchReq) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
match, ok := s.onexbetMatchToTeamMatch[in.MatchID]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
match.IsInplay = false
|
||||
}
|
||||
*/
|
||||
|
||||
func (s *Daemon) onOnexbetTeamLiveMatches(conn *ws.Conn, in onexbet.LiveMatchesMessage) {
|
||||
//s.logger.Println("onOnexbetTeamLiveMatches")
|
||||
//s.logger.Printf("onexbetMatchToTeamMatch size: %d\n", len(s.onexbetMatchToTeamMatch))
|
||||
|
||||
sportID, ok := onexbetSportToFlashscoreSport[in.SportID]
|
||||
if !ok {
|
||||
s.logger.Printf("Bug: unknown OnexbetSport %d\n", in.SportID)
|
||||
return
|
||||
}
|
||||
|
||||
s.logger.Printf("Onexbet live matches: %d, watching: %d; Inplay: %d\n",
|
||||
len(in.Matches), len(in.Watching), len(s.onexbetMatchToTeamMatch))
|
||||
|
||||
liveMap := make(map[string]onexbet.LiveMatch)
|
||||
|
||||
// В цикле рашем 3 задачи:
|
||||
// - формируем словарь из live матчей на сайте 1xBet, для удобства дальнейшей обработки
|
||||
// - для матчей в inplay проверяем не появился ли liveMatch.StatsMatchID, по которому
|
||||
// можно будет узнать результат матча на сайте 1xBet
|
||||
// - пытаемся распознать новые матчи
|
||||
for _, liveMatch := range in.Matches {
|
||||
liveMap[liveMatch.MatchID] = liveMatch
|
||||
|
||||
match, ok := s.onexbetMatchToTeamMatch[liveMatch.MatchID]
|
||||
if ok {
|
||||
s.logger.Printf("Match (%s - %s) already identified\n", match.Home.CanonicalName, match.Away.CanonicalName)
|
||||
// ВАЖНО!
|
||||
// liveMatch.StatsMatchID появится когда матч перейдет в лайв.
|
||||
if match.OnexbetStatsMatchID == "" && liveMatch.StatsMatchID != "" {
|
||||
match.OnexbetStatsMatchID = liveMatch.StatsMatchID
|
||||
match.Score = liveMatch.Score
|
||||
match.ScoreByPeriods = liveMatch.ScoreByPeriods
|
||||
match.CurrentPeriod = liveMatch.CurrentPeriod
|
||||
}
|
||||
} else {
|
||||
if !s.identifiedMatches[liveMatch.MatchID] {
|
||||
// Матча нет среди распознанных - пытаемся опознать
|
||||
s.tryLinkToLiveMatch(sportID, liveMatch)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var watchingMap = make(map[string]bool)
|
||||
|
||||
for _, onexbetMatchID := range in.Watching {
|
||||
watchingMap[onexbetMatchID] = true
|
||||
|
||||
_, isMatchInInplay := s.onexbetMatchToTeamMatch[onexbetMatchID]
|
||||
if !isMatchInInplay {
|
||||
// Матча больше нет в inplay. Если 1xbet присылает его в Watching -
|
||||
// значит API-запрос Unwatch где-то потерялся. Поэтому отправляем снова.
|
||||
s.onexbetAPIClient.Call(api.CallReq{
|
||||
FuncName: "unwatch",
|
||||
In: onexbet.WatchReq{
|
||||
SportID: in.SportID,
|
||||
MatchID: onexbetMatchID,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Цикл по inplay матчам
|
||||
//if sportID == flashscore.Tennis {
|
||||
//
|
||||
//s.checkInplayTennisMatches(liveMap, watchingMap)
|
||||
//} else {
|
||||
s.checkInplayTeamMatches(sportID, in.SportID, liveMap, watchingMap)
|
||||
//}
|
||||
}
|
||||
|
||||
//func (s *Daemon) checkInplayTennisMatches(liveMap map[string]bool, watchingMap map[string]bool) {
|
||||
//for onexbetMatchID, match := range s.onexbetMatchToTeamMatch {
|
||||
//if match.
|
||||
//}
|
||||
//}
|
||||
|
||||
func (s *Daemon) checkInplayTeamMatches(sportID int, onexbetSportID onexbet.OnexbetSport, liveMap map[string]onexbet.LiveMatch, watchingMap map[string]bool) {
|
||||
for onexbetMatchID, match := range s.onexbetMatchToTeamMatch {
|
||||
if match.SportID != sportID {
|
||||
// Если другой спорт - пропускаем матч
|
||||
continue
|
||||
}
|
||||
|
||||
liveMatch, isIn1xbetLive := liveMap[onexbetMatchID]
|
||||
|
||||
if !isIn1xbetLive {
|
||||
// МАТЧИ могут просто исчезать из ЛАЙВ списка, поэтому просто удалять
|
||||
// нельзя! Сперва запросим результат матча у 1xbet и решим что делать.
|
||||
//err := s.processMatchDisappearedFromLive(match, onexbetMatchID)
|
||||
//if err != nil {
|
||||
// s.logger.Printf("processMatchDisappearedFromLive: %s\n", err)
|
||||
//}
|
||||
|
||||
s.logger.Printf("Inplay match (%s - %s) not found in 1xbet live matches. Remove from inplay\n",
|
||||
match.Home.CanonicalName, match.Away.CanonicalName)
|
||||
|
||||
s.removeMatchFromInplay(match, onexbetMatchID)
|
||||
} else {
|
||||
if !watchingMap[onexbetMatchID] {
|
||||
// Матч есть в лайве 1xbet (liveMap) - но нет в наблюдаемых. Например,
|
||||
// перезагрузился 1xbet демон, или отписались.
|
||||
if len(match.Strategies) > 0 {
|
||||
// Если еще есть не сработавшие стратегии - начинаем наблюдать.
|
||||
//s.logger.Printf("Watch: %d, %s\n", in.SportID, onexbetMatchID)
|
||||
|
||||
// Подписываемся на матчи, которые только что идентифицировали + на те, которые
|
||||
// пропали из watch списка (например из-за перезагрузки 1xBET демона)
|
||||
|
||||
for _, strategy := range match.Strategies {
|
||||
if strategy.IsNeedToWatch(liveMatch) {
|
||||
// Если хотя бы одной стратегии нужно наблюдать за матчем
|
||||
// - подписываемся и прерываем цикл
|
||||
match.IsInplay = true
|
||||
|
||||
s.onexbetAPIClient.Call(api.CallReq{
|
||||
FuncName: "watch",
|
||||
In: onexbet.WatchReq{
|
||||
SportID: onexbetSportID,
|
||||
MatchID: onexbetMatchID,
|
||||
},
|
||||
})
|
||||
|
||||
s.mutex.Lock()
|
||||
// Матч дабавили в inplay
|
||||
s.publisher.Publish(
|
||||
getChannel(match.SportID),
|
||||
WSMessageInplayStatusChanged,
|
||||
InplayStatusMessage{
|
||||
MatchID: match.MatchID,
|
||||
IsInplay: match.IsInplay,
|
||||
IsLinkedToOnexbet: match.IsLinkedToOnexbet,
|
||||
},
|
||||
)
|
||||
s.mutex.Unlock()
|
||||
// ВАЖНО!
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s.mutex.Lock()
|
||||
//s.logger.Printf("Unwatch: %d, %s\n", in.SportID, onexbetMatchID)
|
||||
// Матч больше не интересен
|
||||
// Все стратегии сработали - отписываемся от матча
|
||||
delete(s.onexbetMatchToTeamMatch, onexbetMatchID)
|
||||
match.IsInplay = false
|
||||
|
||||
s.onexbetAPIClient.Call(api.CallReq{
|
||||
FuncName: "unwatch",
|
||||
In: onexbet.WatchReq{
|
||||
SportID: onexbetSportID,
|
||||
MatchID: onexbetMatchID,
|
||||
},
|
||||
})
|
||||
|
||||
// Матч убрали из inplay
|
||||
s.publisher.Publish(
|
||||
getChannel(match.SportID),
|
||||
WSMessageInplayStatusChanged,
|
||||
InplayStatusMessage{
|
||||
MatchID: match.MatchID,
|
||||
IsInplay: match.IsInplay,
|
||||
IsLinkedToOnexbet: match.IsLinkedToOnexbet,
|
||||
},
|
||||
)
|
||||
s.mutex.Unlock()
|
||||
}
|
||||
} else {
|
||||
// Матч есть в лайве 1xbet (liveMap) и в наблюдаемых - все OK.
|
||||
// Ничего не делаем.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func (s *Daemon) processMatchDisappearedFromLive(match *TeamMatch, onexbetMatchID string) (err error) {
|
||||
if match.OnexbetStatsMatchID == "" {
|
||||
// Ситуация возвожна, когда матч появился на короткий срок, OnexbetStatsMatchID еще
|
||||
// не получил и затем исчез.
|
||||
err = fmt.Errorf("1xBet match %s has empty OnexbetStatsMatchID", onexbetMatchID)
|
||||
return
|
||||
}
|
||||
|
||||
matchResult, err := onexbet.GetTeamMatchResult(match.OnexbetStatsMatchID)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("onexbet.GetTeamMatchResult: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
if matchResult.Status != onexbet.StatusMatchCompleted {
|
||||
// Если матч не завершен - пропускаем. Предполагаем что матч просто
|
||||
// временно исчез из лайва
|
||||
return
|
||||
}
|
||||
// Матч завершен - загружаем прогнозы, чтобы выставить результаты
|
||||
tips, err := s.model.ListMatchTips(match.MatchID)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("model.ListMatchTips: %s; matchID=%s", err, match.MatchID)
|
||||
return
|
||||
}
|
||||
|
||||
if len(tips) > 0 {
|
||||
// На один матч может быть несколько прогнозов, поэтому важно знать что результаты
|
||||
// всех прогнозов удалось записать в БД. Пока все результаты записать не удастся -
|
||||
// это метод будет вызыватся снова и снова.
|
||||
for _, tip := range tips {
|
||||
err = s.trySetTipResult(tip, matchResult)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("trySetTipResult: %s; tipID=%d", err, tip.TipID)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
// Результат всех прогнозов по матчу успешно записан либо прогнозов не было.
|
||||
// В любом случае - удаляем матч из лайва.
|
||||
s.removeMatchFromInplay(match, onexbetMatchID)
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
func (s *Daemon) removeMatchFromInplay(match *TeamMatch, onexbetMatchID string) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
delete(s.onexbetMatchToTeamMatch, onexbetMatchID)
|
||||
match.IsInplay = false
|
||||
|
||||
// Матч убрали из inplay
|
||||
s.publisher.Publish(
|
||||
getChannel(match.SportID),
|
||||
WSMessageInplayStatusChanged,
|
||||
InplayStatusMessage{
|
||||
MatchID: match.MatchID,
|
||||
IsInplay: match.IsInplay,
|
||||
IsLinkedToOnexbet: match.IsLinkedToOnexbet,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
type WhyNot struct {
|
||||
MatchID string `json:"matchID"`
|
||||
Comment string `json:"comment"`
|
||||
Time string `json:"time"`
|
||||
HomeGoals int `json:"homeGoals"`
|
||||
AwayGoals int `json:"awayGoals"`
|
||||
GamePace float64 `json:"gamePace"`
|
||||
GamePaceFrame int `json:"gamePaceFrame"` // count from Zero
|
||||
}
|
||||
|
||||
//func (s *Daemon) onOnexbetTennisLiveMatchData(conn *ws.Conn, upd onexbet.TeamLiveMatchData) {
|
||||
// s.mutex.Lock()
|
||||
// defer s.mutex.Unlock()
|
||||
|
||||
// s.logger.Printf("tennis MatchData: %s\n", upd.MatchID)
|
||||
|
||||
//}
|
||||
/*
|
||||
type TeamLiveMatchData struct {
|
||||
MatchID string `json:"matchID"`
|
||||
HomeAttacks int `json:"homeAttacks"`
|
||||
HomeDangerousAttacks int `json:"homeDangerousAttacks"`
|
||||
HomeShotsOnTarget int `json:"homeShotsOnTarget"`
|
||||
HomeShotsOffTarget int `json:"homeShotsOffTarget"`
|
||||
AwayAttacks int `json:"awayAttacks"`
|
||||
AwayDangerousAttacks int `json:"awayDangerousAttacks"`
|
||||
AwayShotsOnTarget int `json:"awayShotsOnTarget"`
|
||||
AwayShotsOffTarget int `json:"awayShotsOffTarget"`
|
||||
CurrentTime int `json:"currentTime"`
|
||||
HomeGoals int `json:"homeGoals"`
|
||||
AwayGoals int `json:"awayGoals"`
|
||||
Winner *Winner3Way `json:"winner"`
|
||||
Total Total `json:"total"`
|
||||
Handicap Handicap `json:"handicap"`
|
||||
IndividualTotalHome Total `json:"individualTotalHome"`
|
||||
IndividualTotalAway Total `json:"individualTotalAway"`
|
||||
H1 Half `json:"h1"` // 1st half
|
||||
// tennis
|
||||
Winner2Way *Winner2Way `json:"winner2way"`
|
||||
|
||||
// score
|
||||
Score Score `json:"score"`
|
||||
ScoreByPeriods map[int]Score `json:"scoreByPeriods"`
|
||||
CurrentPeriod int `json:"currentPeriod"`
|
||||
Periods map[int]Period `json:"periods"`
|
||||
}
|
||||
*/
|
||||
|
||||
func swapTeamLiveMatchData(d onexbet.TeamLiveMatchData) onexbet.TeamLiveMatchData {
|
||||
d.AwayAttacks, d.HomeAttacks = d.HomeAttacks, d.AwayAttacks
|
||||
d.AwayDangerousAttacks, d.HomeDangerousAttacks = d.HomeDangerousAttacks, d.AwayDangerousAttacks
|
||||
d.AwayShotsOnTarget, d.HomeShotsOnTarget = d.HomeShotsOnTarget, d.AwayShotsOnTarget
|
||||
d.AwayShotsOffTarget, d.HomeShotsOffTarget = d.HomeShotsOffTarget, d.AwayShotsOffTarget
|
||||
d.AwayGoals, d.HomeGoals = d.HomeGoals, d.AwayGoals
|
||||
|
||||
if d.Winner != nil {
|
||||
d.Winner.Away, d.Winner.Home = d.Winner.Home, d.Winner.Away
|
||||
}
|
||||
|
||||
if d.Winner2Way != nil {
|
||||
d.Winner2Way.Away, d.Winner2Way.Home = d.Winner2Way.Home, d.Winner2Way.Away
|
||||
}
|
||||
|
||||
d.Handicap.Away, d.Handicap.Home = d.Handicap.Home, d.Handicap.Away
|
||||
d.IndividualTotalAway, d.IndividualTotalHome = d.IndividualTotalHome, d.IndividualTotalAway
|
||||
|
||||
d.H1.IndividualTotalAway, d.H1.IndividualTotalHome = d.H1.IndividualTotalHome, d.H1.IndividualTotalAway
|
||||
|
||||
d.Score.Away, d.Score.Home = d.Score.Home, d.Score.Away
|
||||
|
||||
for periodNumber, score := range d.ScoreByPeriods {
|
||||
score.Away, score.Home = score.Home, score.Away
|
||||
|
||||
d.ScoreByPeriods[periodNumber] = score
|
||||
}
|
||||
|
||||
for periodNumber, period := range d.Periods {
|
||||
period.Handicap.Away, period.Handicap.Home = period.Handicap.Home, period.Handicap.Away
|
||||
|
||||
d.Periods[periodNumber] = period
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
func (s *Daemon) onOnexbetTeamLiveMatchData(conn *ws.Conn, upd onexbet.TeamLiveMatchData) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
s.logger.Printf("MatchData: %s\n", upd.MatchID)
|
||||
|
||||
// маппинг -
|
||||
match, ok := s.onexbetMatchToTeamMatch[upd.MatchID]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if match.IsTeamsSwapped {
|
||||
upd = swapTeamLiveMatchData(upd)
|
||||
}
|
||||
|
||||
//if match.SportID == flashscore.Handball {
|
||||
//x, _ := json.MarshalIndent(upd, "", " ")
|
||||
//fmt.Printf("MatchID: %s (1xbet MatchID: %s), Time: %d, HomeGoals: %d, AwayGoals: %d\n",
|
||||
// match.MatchID, upd.MatchID, upd.CurrentTime, upd.HomeGoals, upd.AwayGoals)
|
||||
//}
|
||||
|
||||
channel := getChannel(match.SportID)
|
||||
|
||||
//s.logger.Printf("Onexbet TeamLiveMatchData: sportID %d, channel %s\n", match.SportID, channel)
|
||||
|
||||
var (
|
||||
triggeredStrategyIDs []int64
|
||||
comments []string
|
||||
strategyIDs []int
|
||||
)
|
||||
|
||||
for strategyID := range match.Strategies {
|
||||
strategyIDs = append(strategyIDs, int(strategyID))
|
||||
}
|
||||
|
||||
sort.Ints(strategyIDs)
|
||||
|
||||
for _, strategyID := range strategyIDs {
|
||||
|
||||
strategy := match.Strategies[int64(strategyID)]
|
||||
// Если по стратегии уже дали прогноз
|
||||
//if match.EndedStrategies[strategy.ID()] {
|
||||
// continue
|
||||
//}
|
||||
|
||||
whyNot, bet, code := strategy.GetTip(match, upd)
|
||||
|
||||
switch code {
|
||||
case Waiting:
|
||||
// pass
|
||||
//s.logger.Printf("Waiting: %s\n", whyNot)
|
||||
|
||||
// Отправить в websocket
|
||||
comments = append(comments, fmt.Sprintf("%s:<br>%s", strategy.ShortName(), whyNot))
|
||||
|
||||
case Unwatch:
|
||||
s.logger.Printf("Unwatch: %s\n", whyNot)
|
||||
|
||||
triggeredStrategyIDs = append(triggeredStrategyIDs, strategy.ID())
|
||||
|
||||
comments = append(comments, fmt.Sprintf("%s:<br>%s", strategy.ShortName(), whyNot))
|
||||
|
||||
case Bet:
|
||||
|
||||
triggeredStrategyIDs = append(triggeredStrategyIDs, strategy.ID())
|
||||
|
||||
var err error
|
||||
//var tip model.Tip
|
||||
//var tipPrice float64
|
||||
|
||||
tip := model.Tip{
|
||||
SportID: int64(match.SportID),
|
||||
StrategyID: strategy.ID(),
|
||||
MatchID: match.MatchID,
|
||||
TipTime: time.Now().Unix(),
|
||||
ChampID: match.Champ.ChampID,
|
||||
ChampName: match.Champ.Name,
|
||||
MatchName: fmt.Sprintf("%s - %s", match.Home.CanonicalName, match.Away.CanonicalName),
|
||||
Market: bet.Market,
|
||||
Side: bet.Side,
|
||||
Param: bet.Param,
|
||||
Price: bet.Price,
|
||||
CurrentMatchTime: upd.CurrentTime,
|
||||
OnexbetStatsMatchID: match.OnexbetStatsMatchID,
|
||||
IsTeamsSwapped: match.IsTeamsSwapped,
|
||||
}
|
||||
|
||||
champRatings, ok := s.champRatings[match.SportID]
|
||||
if ok {
|
||||
// обновляем рейтинги чемпионатов
|
||||
key := ChampRatingKey{
|
||||
ChampID: tip.ChampID,
|
||||
StrategyID: tip.StrategyID,
|
||||
}
|
||||
|
||||
rating, ok := champRatings[key]
|
||||
if ok {
|
||||
//if (rating.Won+rating.Lost) >= 6 && rating.GetAccuracy() >= 75 {
|
||||
if rating.GetAccuracy() >= 70 {
|
||||
tip.IsRated = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s.logger.Printf("Bet: %#v\n", tip)
|
||||
|
||||
tip.TipID, err = s.model.AddTip(tip)
|
||||
if err != nil {
|
||||
s.logger.Printf("model.AddTip: %s\n", err)
|
||||
} else {
|
||||
// Отправить в websocket
|
||||
// Добавили новый Tip
|
||||
s.publisher.Publish(channel, WSMessageTipsChanged, nil)
|
||||
|
||||
if match.SportID == flashscore.Football {
|
||||
// Для футбола только рейтинговые чемпионаты
|
||||
// Отправить в телеграм
|
||||
if strategy.IsNotifyInTelegram() && tip.IsRated {
|
||||
// Отправлять ли в телеграм
|
||||
msg := fmt.Sprintf(strategy.GetTelegramTipPattern(), tip.TipID, strategy.ID(),
|
||||
match.Zone.Name, match.Champ.Name,
|
||||
match.Home.CanonicalName, match.Away.CanonicalName,
|
||||
bet.Price)
|
||||
|
||||
telegramChatID, hasChat := sportToTelegramChatID[match.SportID]
|
||||
if hasChat {
|
||||
err = sendMessageToTelegramGroup(telegramChatID, msg)
|
||||
if err != nil {
|
||||
s.logger.Printf("send tip to Telegram: %s\n", err)
|
||||
}
|
||||
} else {
|
||||
s.logger.Printf("Sport %d hasn't telegram chat\n", match.SportID)
|
||||
}
|
||||
}
|
||||
} else if match.SportID == flashscore.Handball {
|
||||
if strategy.IsNotifyInTelegram() && tip.IsRated {
|
||||
param, err := strconv.ParseFloat(bet.Param, 64)
|
||||
if err != nil {
|
||||
s.logger.Printf("Wrong bet param: %s; param=%s\n", err, bet.Param)
|
||||
} else {
|
||||
|
||||
var betterParam1, betterParam2 float64
|
||||
|
||||
switch strategy.(type) {
|
||||
case HandballFTOver, HandballP1Over:
|
||||
betterParam1 = param - 1
|
||||
betterParam2 = param - 2
|
||||
|
||||
case HandballFTUnder, HandballP1Under:
|
||||
betterParam1 = param + 1
|
||||
betterParam2 = param + 2
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown strategy %#v\n", strategy))
|
||||
}
|
||||
// Для гандбола отправляем все прогнозы
|
||||
|
||||
msg := fmt.Sprintf(strategy.GetTelegramTipPattern(), tip.TipID, strategy.ID(),
|
||||
match.Zone.Name, match.Champ.Name,
|
||||
match.Home.CanonicalName, match.Away.CanonicalName,
|
||||
param, bet.Price, param, betterParam1, betterParam2)
|
||||
|
||||
telegramChatID, hasChat := sportToTelegramChatID[match.SportID]
|
||||
if hasChat {
|
||||
err = sendMessageToTelegramGroup(telegramChatID, msg)
|
||||
if err != nil {
|
||||
s.logger.Printf("send tip to Telegram: %s\n", err)
|
||||
}
|
||||
} else {
|
||||
s.logger.Printf("Sport %d hasn't telegram chat\n", match.SportID)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if match.SportID == flashscore.Tennis {
|
||||
if strategy.IsNotifyInTelegram() {
|
||||
msg := fmt.Sprintf(bet.TelegramMessage, tip.TipID)
|
||||
|
||||
telegramChatID, hasChat := sportToTelegramChatID[match.SportID]
|
||||
if hasChat {
|
||||
err = sendMessageToTelegramGroup(telegramChatID, msg)
|
||||
if err != nil {
|
||||
s.logger.Printf("send tip to Telegram: %s\n", err)
|
||||
}
|
||||
} else {
|
||||
s.logger.Printf("Sport %d hasn't telegram chat\n", match.SportID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, strategyID := range triggeredStrategyIDs {
|
||||
delete(match.Strategies, strategyID)
|
||||
}
|
||||
|
||||
if len(match.Strategies) == 0 {
|
||||
// Все стратегии сработали - отписываемся от матча
|
||||
|
||||
s.logger.Printf("Unwatch: %d, %s\n", match.SportID, upd.MatchID)
|
||||
|
||||
// Матч больше не интересен
|
||||
delete(s.onexbetMatchToTeamMatch, upd.MatchID)
|
||||
match.IsInplay = false
|
||||
|
||||
s.onexbetAPIClient.Call(api.CallReq{
|
||||
FuncName: "unwatch",
|
||||
In: onexbet.WatchReq{
|
||||
SportID: flashscoreSportToOnexbetSport[match.SportID],
|
||||
MatchID: upd.MatchID, // onexbetMatchID
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if match.SportID == flashscore.Tennis {
|
||||
if len(comments) > 0 {
|
||||
s.publisher.Publish(
|
||||
channel,
|
||||
WSMessageWhyNot,
|
||||
WhyNot{
|
||||
MatchID: match.MatchID,
|
||||
Comment: strings.Join(comments, "<br><br>"),
|
||||
Time: "",
|
||||
HomeGoals: upd.HomeGoals,
|
||||
AwayGoals: upd.AwayGoals,
|
||||
GamePace: match.GamePace,
|
||||
GamePaceFrame: match.GamePaceFrame,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if len(comments) > 0 {
|
||||
s.publisher.Publish(
|
||||
channel,
|
||||
WSMessageWhyNot,
|
||||
WhyNot{
|
||||
MatchID: match.MatchID,
|
||||
Comment: strings.Join(comments, "<br><br>"),
|
||||
Time: fmt.Sprintf("%s", time.Duration(upd.CurrentTime)*time.Second),
|
||||
HomeGoals: upd.HomeGoals,
|
||||
AwayGoals: upd.AwayGoals,
|
||||
GamePace: match.GamePace,
|
||||
GamePaceFrame: match.GamePaceFrame,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
var tipTextPattern string
|
||||
|
||||
switch strategyID {
|
||||
case 1:
|
||||
tipTextPattern = tipTextAlgo1Pattern
|
||||
|
||||
case 2:
|
||||
tipTextPattern = tipTextAlgo2Pattern
|
||||
|
||||
case 3:
|
||||
tipTextPattern = tipTextAlgo3Pattern
|
||||
|
||||
case 100:
|
||||
tipTextPattern = tipTextAlgo100Pattern
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
switch strategyID {
|
||||
case 1:
|
||||
var price float64
|
||||
for _, over := range upd.H1.Total.Over {
|
||||
if over.Param == "0.5" {
|
||||
price = over.Price
|
||||
}
|
||||
}
|
||||
|
||||
tipPrice = price
|
||||
|
||||
tip = model.Tip{
|
||||
SportID: SportFootball,
|
||||
StrategyID: int64(strategyID),
|
||||
MatchID: match.MatchID,
|
||||
TipTime: time.Now().Unix(),
|
||||
ChampName: match.Champ.Name,
|
||||
MatchName: fmt.Sprintf("%s - %s", match.Home.CanonicalName, match.Away.CanonicalName),
|
||||
Market: MarketTotal1stHalf,
|
||||
Side: SideOver,
|
||||
Param: "0.5",
|
||||
Price: price,
|
||||
CurrentMatchTime: upd.CurrentTime,
|
||||
OnexbetStatsMatchID: match.OnexbetStatsMatchID,
|
||||
}
|
||||
|
||||
case 2:
|
||||
var price float64
|
||||
for _, over := range upd.Total.Over {
|
||||
if over.Param == "0.5" {
|
||||
price = over.Price
|
||||
}
|
||||
}
|
||||
|
||||
tipPrice = price
|
||||
|
||||
tip = model.Tip{
|
||||
SportID: SportFootball,
|
||||
StrategyID: int64(strategyID),
|
||||
MatchID: match.MatchID,
|
||||
TipTime: time.Now().Unix(),
|
||||
ChampName: match.Champ.Name,
|
||||
MatchName: fmt.Sprintf("%s - %s", match.Home.CanonicalName, match.Away.CanonicalName),
|
||||
Market: MarketTotal,
|
||||
Side: SideOver,
|
||||
Param: "0.5",
|
||||
Price: price,
|
||||
CurrentMatchTime: upd.CurrentTime,
|
||||
OnexbetStatsMatchID: match.OnexbetStatsMatchID,
|
||||
}
|
||||
|
||||
case 3:
|
||||
var price float64
|
||||
for _, over := range upd.Total.Over {
|
||||
if over.Param == "1.5" {
|
||||
price = over.Price
|
||||
}
|
||||
}
|
||||
|
||||
tipPrice = price
|
||||
|
||||
tip = model.Tip{
|
||||
SportID: SportFootball,
|
||||
StrategyID: int64(strategyID),
|
||||
MatchID: match.MatchID,
|
||||
TipTime: time.Now().Unix(),
|
||||
ChampName: match.Champ.Name,
|
||||
MatchName: fmt.Sprintf("%s - %s", match.Home.CanonicalName, match.Away.CanonicalName),
|
||||
Market: MarketTotal,
|
||||
Side: SideOver,
|
||||
Param: "1.5",
|
||||
Price: price,
|
||||
CurrentMatchTime: upd.CurrentTime,
|
||||
OnexbetStatsMatchID: match.OnexbetStatsMatchID,
|
||||
}
|
||||
|
||||
case 100:
|
||||
var price float64
|
||||
for _, over := range upd.H1.Total.Over {
|
||||
if over.Param == "0.5" {
|
||||
price = over.Price
|
||||
}
|
||||
}
|
||||
|
||||
tipPrice = price
|
||||
|
||||
tip = model.Tip{
|
||||
SportID: SportFootball,
|
||||
StrategyID: int64(strategyID),
|
||||
MatchID: match.MatchID,
|
||||
TipTime: time.Now().Unix(),
|
||||
ChampName: match.Champ.Name,
|
||||
MatchName: fmt.Sprintf("%s - %s", match.Home.CanonicalName, match.Away.CanonicalName),
|
||||
Market: MarketTotal1stHalf,
|
||||
Side: SideOver,
|
||||
Param: "0.5",
|
||||
Price: price,
|
||||
CurrentMatchTime: upd.CurrentTime,
|
||||
OnexbetStatsMatchID: match.OnexbetStatsMatchID,
|
||||
}
|
||||
}
|
||||
*/
|
||||
Reference in New Issue
Block a user