876 lines
28 KiB
Go
Executable File
876 lines
28 KiB
Go
Executable File
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,
|
||
}
|
||
}
|
||
*/
|