Compare commits
	
		
			1 Commits
		
	
	
		
			12fce31ed8
			...
			b7463efb45
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| b7463efb45 | 
							
								
								
									
										26
									
								
								context.go
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								context.go
									
									
									
									
									
								
							| @ -20,7 +20,7 @@ const ( | |||||||
| 	ctxKeyMessageCleaner | 	ctxKeyMessageCleaner | ||||||
| 	ctxKeyDataBase | 	ctxKeyDataBase | ||||||
| 	ctxKeyPlayersBoard | 	ctxKeyPlayersBoard | ||||||
| 	ctxKeyFragMessages | 	ctxKeyMessageStorage | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func createLogger(debug bool) *logrus.Logger { | func createLogger(debug bool) *logrus.Logger { | ||||||
| @ -44,17 +44,16 @@ func createLogger(debug bool) *logrus.Logger { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func createContext(config *Config, bot *tgbotapi.BotAPI, mc *messageCleaner, ch *commandHandler, db *dbLayer, pb *playersBoard, fms *fragMessages) (context.Context, func()) { | func createContext(config *Config, bot *tgbotapi.BotAPI, ch *commandHandler, db *dbLayer, pb *playersBoard, ms *messageStorage) (context.Context, func()) { | ||||||
| 	ctx, cancelFunc := context.WithCancel(context.Background()) | 	ctx, cancelFunc := context.WithCancel(context.Background()) | ||||||
|  |  | ||||||
| 	ctx = context.WithValue(ctx, ctxKeyLogger, createLogger(true)) | 	ctx = context.WithValue(ctx, ctxKeyLogger, createLogger(true)) | ||||||
| 	ctx = context.WithValue(ctx, ctxKeyBotApi, bot) | 	ctx = context.WithValue(ctx, ctxKeyBotApi, bot) | ||||||
| 	ctx = context.WithValue(ctx, ctxKeyConfig, config) | 	ctx = context.WithValue(ctx, ctxKeyConfig, config) | ||||||
| 	ctx = context.WithValue(ctx, ctxKeyMessageCleaner, mc) |  | ||||||
| 	ctx = context.WithValue(ctx, ctxKeyCommandHandler, ch) | 	ctx = context.WithValue(ctx, ctxKeyCommandHandler, ch) | ||||||
| 	ctx = context.WithValue(ctx, ctxKeyDataBase, db) | 	ctx = context.WithValue(ctx, ctxKeyDataBase, db) | ||||||
| 	ctx = context.WithValue(ctx, ctxKeyPlayersBoard, pb) | 	ctx = context.WithValue(ctx, ctxKeyPlayersBoard, pb) | ||||||
| 	ctx = context.WithValue(ctx, ctxKeyFragMessages, fms) | 	ctx = context.WithValue(ctx, ctxKeyMessageStorage, ms) | ||||||
|  |  | ||||||
| 	return ctx, cancelFunc | 	return ctx, cancelFunc | ||||||
| } | } | ||||||
| @ -86,15 +85,6 @@ func getConfig(ctx context.Context) *Config { | |||||||
| 	panic("Failed to get config from ctx") | 	panic("Failed to get config from ctx") | ||||||
| } | } | ||||||
|  |  | ||||||
| func getMessageCleaner(ctx context.Context) *messageCleaner { |  | ||||||
| 	v := ctx.Value(ctxKeyMessageCleaner) |  | ||||||
| 	if mc, ok := v.(*messageCleaner); ok { |  | ||||||
| 		return mc |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	panic("Failed to get message cleaner from ctx") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func getCommandHandler(ctx context.Context) *commandHandler { | func getCommandHandler(ctx context.Context) *commandHandler { | ||||||
| 	v := ctx.Value(ctxKeyCommandHandler) | 	v := ctx.Value(ctxKeyCommandHandler) | ||||||
| 	if ch, ok := v.(*commandHandler); ok { | 	if ch, ok := v.(*commandHandler); ok { | ||||||
| @ -122,11 +112,11 @@ func getDPlayersBoard(ctx context.Context) *playersBoard { | |||||||
| 	panic("Failed to get players board from ctx") | 	panic("Failed to get players board from ctx") | ||||||
| } | } | ||||||
|  |  | ||||||
| func getFragMessages(ctx context.Context) *fragMessages { | func getMessageStorage(ctx context.Context) *messageStorage { | ||||||
| 	v := ctx.Value(ctxKeyFragMessages) | 	v := ctx.Value(ctxKeyMessageStorage) | ||||||
| 	if fms, ok := v.(*fragMessages); ok { | 	if ms, ok := v.(*messageStorage); ok { | ||||||
| 		return fms | 		return ms | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	panic("Failed to get frag messages from ctx") | 	panic("Failed to get message storage from ctx") | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,90 +0,0 @@ | |||||||
| package main |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"fmt" |  | ||||||
| 	"sync" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type fragMessage struct { |  | ||||||
| 	playerName     string |  | ||||||
| 	entityName     string |  | ||||||
| 	chatID         int64 |  | ||||||
| 	messageID      int |  | ||||||
| 	fragCount      int |  | ||||||
| 	latestFragTime time.Time |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type fragMessages struct { |  | ||||||
| 	mx       sync.Mutex |  | ||||||
| 	messages []*fragMessage |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (f *fragMessage) increaseFrag() *fragMessage { |  | ||||||
| 	f.fragCount += 1 |  | ||||||
| 	f.latestFragTime = time.Now() |  | ||||||
| 	return f |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func newFragMessages() *fragMessages { |  | ||||||
| 	return &fragMessages{ |  | ||||||
| 		messages: make([]*fragMessage, 0), |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (fms *fragMessages) addFrag(ctx context.Context, playerName string, entityName string) error { |  | ||||||
| 	botApi := getBotApi(ctx) |  | ||||||
| 	cfg := getConfig(ctx) |  | ||||||
| 	mc := getMessageCleaner(ctx) |  | ||||||
|  |  | ||||||
| 	fms.mx.Lock() |  | ||||||
| 	defer fms.mx.Unlock() |  | ||||||
|  |  | ||||||
| 	for _, fm := range fms.messages { |  | ||||||
| 		if fm.playerName == playerName && fm.entityName == entityName { |  | ||||||
| 			mc.resetTimer(fm.messageID, fm.chatID, time.Second*30) |  | ||||||
| 			fm.increaseFrag() |  | ||||||
| 			mess := tgbotapi.NewEditMessageText( |  | ||||||
| 				fm.chatID, |  | ||||||
| 				fm.messageID, |  | ||||||
| 				fmt.Sprintf("%s убил %s ✗ *%d*", fm.playerName, fm.entityName, fm.fragCount), |  | ||||||
| 			) |  | ||||||
| 			mess.ParseMode = tgbotapi.ModeMarkdown |  | ||||||
| 			m, err := botApi.Send(mess) |  | ||||||
| 			if err != nil { |  | ||||||
| 				return err |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			if m.MessageID != fm.messageID { |  | ||||||
| 				m.MessageID = fm.messageID |  | ||||||
| 				getLogger(ctx).Info("message id was changed") |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			return nil |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	mess := tgbotapi.NewMessage(cfg.ChatID, fmt.Sprintf("%s убил %s", playerName, entityName)) |  | ||||||
| 	m, err := botApi.Send(mess) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	mc.add(ctx, m.MessageID, cfg.ChatID, time.Second*30) |  | ||||||
|  |  | ||||||
| 	fm := &fragMessage{ |  | ||||||
| 		playerName:     playerName, |  | ||||||
| 		entityName:     entityName, |  | ||||||
| 		chatID:         cfg.ChatID, |  | ||||||
| 		messageID:      m.MessageID, |  | ||||||
| 		fragCount:      1, |  | ||||||
| 		latestFragTime: time.Now(), |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	fms.messages = append(fms.messages, fm) |  | ||||||
|  |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
							
								
								
									
										69
									
								
								handlers.go
									
									
									
									
									
								
							
							
						
						
									
										69
									
								
								handlers.go
									
									
									
									
									
								
							| @ -4,8 +4,6 @@ import ( | |||||||
| 	"context" | 	"context" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type command interface { | type command interface { | ||||||
| @ -42,19 +40,21 @@ type joinCommand struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (c *joinCommand) run(ctx context.Context) { | func (c *joinCommand) run(ctx context.Context) { | ||||||
| 	botApi := getBotApi(ctx) |  | ||||||
| 	cfg := getConfig(ctx) |  | ||||||
| 	mc := getMessageCleaner(ctx) |  | ||||||
| 	db := getDB(ctx) | 	db := getDB(ctx) | ||||||
| 	log := getLogger(ctx) | 	log := getLogger(ctx) | ||||||
| 	pb := getDPlayersBoard(ctx) | 	pb := getDPlayersBoard(ctx) | ||||||
|  | 	ms := getMessageStorage(ctx) | ||||||
|  |  | ||||||
|  | 	err := ms.apply(ctx, fmt.Sprintf("join:%s", c.name), time.Minute/2, func(d interface{}) (string, interface{}) { | ||||||
|  | 		if v, _ := d.(bool); v { | ||||||
|  | 			return fmt.Sprintf("%s подключился снова", c.name), true | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return fmt.Sprintf("%s подключился", c.name), true | ||||||
|  | 	}) | ||||||
|  |  | ||||||
| 	mess := tgbotapi.NewMessage(cfg.ChatID, fmt.Sprintf("%s подключился", c.name)) |  | ||||||
| 	m, err := botApi.Send(mess) |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error(err) | 		log.Error(err) | ||||||
| 	} else { |  | ||||||
| 		mc.add(ctx, m.MessageID, cfg.ChatID, time.Second*30) |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	p, err := db.getPlayerByName(ctx, c.name) | 	p, err := db.getPlayerByName(ctx, c.name) | ||||||
| @ -90,19 +90,21 @@ type quitCommand struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (c *quitCommand) run(ctx context.Context) { | func (c *quitCommand) run(ctx context.Context) { | ||||||
| 	botApi := getBotApi(ctx) |  | ||||||
| 	cfg := getConfig(ctx) |  | ||||||
| 	mc := getMessageCleaner(ctx) |  | ||||||
| 	db := getDB(ctx) | 	db := getDB(ctx) | ||||||
| 	log := getLogger(ctx) | 	log := getLogger(ctx) | ||||||
| 	pb := getDPlayersBoard(ctx) | 	pb := getDPlayersBoard(ctx) | ||||||
|  | 	ms := getMessageStorage(ctx) | ||||||
|  |  | ||||||
|  | 	err := ms.apply(ctx, fmt.Sprintf("quit:%s", c.name), time.Minute/2, func(d interface{}) (string, interface{}) { | ||||||
|  | 		if v, _ := d.(bool); v { | ||||||
|  | 			return fmt.Sprintf("%s отключился снова", c.name), true | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return fmt.Sprintf("%s отключился", c.name), true | ||||||
|  | 	}) | ||||||
|  |  | ||||||
| 	mess := tgbotapi.NewMessage(cfg.ChatID, fmt.Sprintf("%s отключился", c.name)) |  | ||||||
| 	m, err := botApi.Send(mess) |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error(err) | 		log.Error(err) | ||||||
| 	} else { |  | ||||||
| 		mc.add(ctx, m.MessageID, cfg.ChatID, time.Second*30) |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	p, err := db.getPlayerByName(ctx, c.name) | 	p, err := db.getPlayerByName(ctx, c.name) | ||||||
| @ -138,6 +140,19 @@ func (c *deathCommand) run(ctx context.Context) { | |||||||
| 	db := getDB(ctx) | 	db := getDB(ctx) | ||||||
| 	log := getLogger(ctx) | 	log := getLogger(ctx) | ||||||
| 	pb := getDPlayersBoard(ctx) | 	pb := getDPlayersBoard(ctx) | ||||||
|  | 	ms := getMessageStorage(ctx) | ||||||
|  |  | ||||||
|  | 	err := ms.apply(ctx, fmt.Sprintf("death:%s", c.name), time.Minute/2, func(d interface{}) (string, interface{}) { | ||||||
|  | 		if v, _ := d.(bool); v { | ||||||
|  | 			return fmt.Sprintf("%s умудрился опять помереть от %s", c.name, c.deathType), true | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return fmt.Sprintf("%s пал смертью храбрых от %s", c.name, c.deathType), true | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Error(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	p, err := db.getPlayerByName(ctx, c.name) | 	p, err := db.getPlayerByName(ctx, c.name) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -165,12 +180,7 @@ func (c *killCommand) run(ctx context.Context) { | |||||||
| 	db := getDB(ctx) | 	db := getDB(ctx) | ||||||
| 	log := getLogger(ctx) | 	log := getLogger(ctx) | ||||||
| 	pb := getDPlayersBoard(ctx) | 	pb := getDPlayersBoard(ctx) | ||||||
| 	fms := getFragMessages(ctx) | 	ms := getMessageStorage(ctx) | ||||||
|  |  | ||||||
| 	err := fms.addFrag(ctx, c.name, c.entity) |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Error(err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	p, err := db.getPlayerByName(ctx, c.name) | 	p, err := db.getPlayerByName(ctx, c.name) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -183,6 +193,21 @@ func (c *killCommand) run(ctx context.Context) { | |||||||
| 		log.Error(err) | 		log.Error(err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	f := func(d interface{}) (string, interface{}) { | ||||||
|  | 		if fragsNum, ok := d.(int); ok { | ||||||
|  | 			fragsNum++ | ||||||
|  |  | ||||||
|  | 			return fmt.Sprintf("%s убил %s ✗ *%d*", p.name, c.entity, fragsNum), fragsNum | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return fmt.Sprintf("%s убил %s", p.name, c.entity), 1 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = ms.apply(ctx, fmt.Sprintf("kill:%s:%s", p.id, c.entity), time.Minute/2, f) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Error(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	err = pb.update(ctx, achievementMaxFrags, achievementPeaceable) | 	err = pb.update(ctx, achievementMaxFrags, achievementPeaceable) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error(err) | 		log.Error(err) | ||||||
|  | |||||||
							
								
								
									
										30
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								main.go
									
									
									
									
									
								
							| @ -55,15 +55,11 @@ func main() { | |||||||
| 		os.Exit(5) | 		os.Exit(5) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	mc := newMessageCleaner(bot, &wg) |  | ||||||
|  |  | ||||||
| 	ch := newCommandHandler() | 	ch := newCommandHandler() | ||||||
|  |  | ||||||
| 	pb := newPlayersBoard() | 	pb := newPlayersBoard() | ||||||
|  | 	ms := NewMessageStorage() | ||||||
|  |  | ||||||
| 	fms := newFragMessages() | 	ctx, cancelFunc := createContext(&cfg, bot, ch, db, pb, ms) | ||||||
|  |  | ||||||
| 	ctx, cancelFunc := createContext(&cfg, bot, mc, ch, db, pb, fms) |  | ||||||
|  |  | ||||||
| 	getLogger(ctx).Infof("Run...") | 	getLogger(ctx).Infof("Run...") | ||||||
|  |  | ||||||
| @ -73,24 +69,14 @@ func main() { | |||||||
| 		os.Exit(5) | 		os.Exit(5) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	runPlayerBoard(ctx, pb, &wg) |  | ||||||
| 	runCommandHandler(ctx, ch, &wg) | 	runCommandHandler(ctx, ch, &wg) | ||||||
|  | 	runMessageStorage(ctx, ms, &wg) | ||||||
| 	runHttpServer(ctx, cfg.Http, &wg) | 	runHttpServer(ctx, cfg.Http, &wg) | ||||||
| 	runSignalHandler(ctx, cancelFunc) | 	runSignalHandler(ctx, cancelFunc) | ||||||
|  |  | ||||||
| 	wg.Wait() | 	wg.Wait() | ||||||
| } | } | ||||||
|  |  | ||||||
| func runPlayerBoard(ctx context.Context, pb *playersBoard, wg *sync.WaitGroup) { |  | ||||||
| 	wg.Add(1) |  | ||||||
|  |  | ||||||
| 	go func() { |  | ||||||
| 		defer wg.Done() |  | ||||||
|  |  | ||||||
| 		pb.run(ctx) |  | ||||||
| 	}() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func runCommandHandler(ctx context.Context, ch *commandHandler, wg *sync.WaitGroup) { | func runCommandHandler(ctx context.Context, ch *commandHandler, wg *sync.WaitGroup) { | ||||||
| 	wg.Add(1) | 	wg.Add(1) | ||||||
|  |  | ||||||
| @ -101,6 +87,16 @@ func runCommandHandler(ctx context.Context, ch *commandHandler, wg *sync.WaitGro | |||||||
| 	}() | 	}() | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func runMessageStorage(ctx context.Context, ms *messageStorage, wg *sync.WaitGroup) { | ||||||
|  | 	wg.Add(1) | ||||||
|  |  | ||||||
|  | 	go func() { | ||||||
|  | 		defer wg.Done() | ||||||
|  |  | ||||||
|  | 		ms.run(ctx) | ||||||
|  | 	}() | ||||||
|  | } | ||||||
|  |  | ||||||
| func runHttpServer(ctx context.Context, addr string, wg *sync.WaitGroup) { | func runHttpServer(ctx context.Context, addr string, wg *sync.WaitGroup) { | ||||||
| 	server := http.Server{ | 	server := http.Server{ | ||||||
| 		Addr:    addr, | 		Addr:    addr, | ||||||
|  | |||||||
| @ -1,91 +0,0 @@ | |||||||
| package main |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"sync" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type messageToClean struct { |  | ||||||
| 	messID int |  | ||||||
| 	chatID int64 |  | ||||||
| 	timer  *time.Timer |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type messageCleaner struct { |  | ||||||
| 	botApi   *tgbotapi.BotAPI |  | ||||||
| 	wg       *sync.WaitGroup |  | ||||||
| 	messages []*messageToClean |  | ||||||
| 	mx       sync.Mutex |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func newMessageCleaner(bot *tgbotapi.BotAPI, wg *sync.WaitGroup) *messageCleaner { |  | ||||||
| 	return &messageCleaner{ |  | ||||||
| 		botApi:   bot, |  | ||||||
| 		wg:       wg, |  | ||||||
| 		messages: make([]*messageToClean, 0), |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *messageCleaner) add(ctx context.Context, messID int, chatID int64, d time.Duration) { |  | ||||||
| 	m.mx.Lock() |  | ||||||
| 	defer m.mx.Unlock() |  | ||||||
|  |  | ||||||
| 	for _, ms := range m.messages { |  | ||||||
| 		if ms.chatID == chatID && ms.messID == messID { |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	m.wg.Add(1) |  | ||||||
|  |  | ||||||
| 	go func() { |  | ||||||
| 		defer m.wg.Done() |  | ||||||
|  |  | ||||||
| 		ms := &messageToClean{ |  | ||||||
| 			messID: messID, |  | ||||||
| 			chatID: chatID, |  | ||||||
| 			timer:  time.NewTimer(d), |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		m.messages = append(m.messages, ms) |  | ||||||
|  |  | ||||||
| 		select { |  | ||||||
| 		case <-ms.timer.C: |  | ||||||
| 		case <-ctx.Done(): |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		m.remove(messID, chatID) |  | ||||||
|  |  | ||||||
| 		_, _ = m.botApi.DeleteMessage(tgbotapi.DeleteMessageConfig{ |  | ||||||
| 			MessageID: messID, |  | ||||||
| 			ChatID:    chatID, |  | ||||||
| 		}) |  | ||||||
| 	}() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *messageCleaner) remove(messID int, chatID int64) { |  | ||||||
| 	m.mx.Lock() |  | ||||||
| 	defer m.mx.Unlock() |  | ||||||
|  |  | ||||||
| 	for i, ms := range m.messages { |  | ||||||
| 		if ms.chatID == chatID && ms.messID == messID { |  | ||||||
| 			m.messages = append(m.messages[:i], m.messages[i+1:]...) |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *messageCleaner) resetTimer(messID int, chatID int64, d time.Duration) { |  | ||||||
| 	m.mx.Lock() |  | ||||||
| 	defer m.mx.Unlock() |  | ||||||
|  |  | ||||||
| 	for _, ms := range m.messages { |  | ||||||
| 		if ms.chatID == chatID && ms.messID == messID { |  | ||||||
| 			ms.timer.Reset(d) |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										131
									
								
								messageStorage.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								messageStorage.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,131 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"sync" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type messageData struct { | ||||||
|  | 	key    string | ||||||
|  | 	messID int | ||||||
|  | 	chatID int64 | ||||||
|  | 	data   interface{} | ||||||
|  | 	timer  *time.Timer | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type messageStorage struct { | ||||||
|  | 	m        sync.Mutex | ||||||
|  | 	messages []*messageData | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewMessageStorage() *messageStorage { | ||||||
|  | 	return &messageStorage{ | ||||||
|  | 		messages: make([]*messageData, 0, 20), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func deleteMessageFromTelegram(ctx context.Context, messID int, chatID int64) { | ||||||
|  | 	botApi := getBotApi(ctx) | ||||||
|  |  | ||||||
|  | 	_, err := botApi.DeleteMessage(tgbotapi.DeleteMessageConfig{ | ||||||
|  | 		MessageID: messID, | ||||||
|  | 		ChatID:    chatID, | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		getLogger(ctx).Error(err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (ms *messageStorage) run(ctx context.Context) { | ||||||
|  | 	<-ctx.Done() | ||||||
|  |  | ||||||
|  | 	ms.m.Lock() | ||||||
|  | 	defer ms.m.Unlock() | ||||||
|  |  | ||||||
|  | 	for _, m := range ms.messages { | ||||||
|  | 		deleteMessageFromTelegram(ctx, m.messID, m.chatID) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ms.messages = ms.messages[:] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (ms *messageStorage) runTimeCleaner(ctx context.Context, md *messageData, d time.Duration) { | ||||||
|  | 	if d <= 0 { | ||||||
|  | 		md.timer = nil | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	t := time.NewTimer(d) | ||||||
|  | 	md.timer = t | ||||||
|  |  | ||||||
|  | 	go func() { | ||||||
|  | 		<-t.C | ||||||
|  |  | ||||||
|  | 		ms.m.Lock() | ||||||
|  | 		defer ms.m.Unlock() | ||||||
|  |  | ||||||
|  | 		if md.timer == t { | ||||||
|  | 			for i, m := range ms.messages { | ||||||
|  | 				if m == md { | ||||||
|  | 					deleteMessageFromTelegram(ctx, m.messID, m.chatID) | ||||||
|  | 					ms.messages = append(ms.messages[:i], ms.messages[i+1:]...) | ||||||
|  |  | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type messageChanger func(interface{}) (string, interface{}) | ||||||
|  |  | ||||||
|  | func (ms *messageStorage) apply(ctx context.Context, key string, d time.Duration, mc messageChanger) error { | ||||||
|  | 	ms.m.Lock() | ||||||
|  | 	defer ms.m.Unlock() | ||||||
|  |  | ||||||
|  | 	botApi := getBotApi(ctx) | ||||||
|  | 	cfg := getConfig(ctx) | ||||||
|  |  | ||||||
|  | 	for _, md := range ms.messages { | ||||||
|  | 		if md.key == key { | ||||||
|  | 			text, data := mc(md.data) | ||||||
|  |  | ||||||
|  | 			mess := tgbotapi.NewEditMessageText(md.chatID, md.messID, text) | ||||||
|  | 			mess.ParseMode = tgbotapi.ModeMarkdown | ||||||
|  | 			_, err := botApi.Send(mess) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			md.data = data | ||||||
|  | 			ms.runTimeCleaner(ctx, md, d) | ||||||
|  |  | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	text, data := mc(nil) | ||||||
|  |  | ||||||
|  | 	mess := tgbotapi.NewMessage(cfg.ChatID, text) | ||||||
|  | 	mess.ParseMode = tgbotapi.ModeMarkdown | ||||||
|  | 	m, err := botApi.Send(mess) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	md := &messageData{ | ||||||
|  | 		key:    key, | ||||||
|  | 		messID: m.MessageID, | ||||||
|  | 		chatID: cfg.ChatID, | ||||||
|  | 		data:   data, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ms.messages = append(ms.messages, md) | ||||||
|  | 	ms.runTimeCleaner(ctx, md, d) | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
| @ -7,9 +7,7 @@ import ( | |||||||
| 	"sync" | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api" |  | ||||||
| 	"github.com/hako/durafmt" | 	"github.com/hako/durafmt" | ||||||
| 	"github.com/pkg/errors" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type achievement uint8 | type achievement uint8 | ||||||
| @ -54,7 +52,6 @@ var allAchievement = []achievement{ | |||||||
|  |  | ||||||
| type playerInfo struct { | type playerInfo struct { | ||||||
| 	playerID     string | 	playerID     string | ||||||
| 	playerMessageID int |  | ||||||
| 	isOnline     bool | 	isOnline     bool | ||||||
| 	achievements []achievement | 	achievements []achievement | ||||||
| 	messageText  string | 	messageText  string | ||||||
| @ -74,8 +71,6 @@ const ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| func (p *playerInfo) updatePlayerInfo(ctx context.Context) error { | func (p *playerInfo) updatePlayerInfo(ctx context.Context) error { | ||||||
| 	botApi := getBotApi(ctx) |  | ||||||
| 	conf := getConfig(ctx) |  | ||||||
| 	db := getDB(ctx) | 	db := getDB(ctx) | ||||||
|  |  | ||||||
| 	lines := make([]string, 0, 10) | 	lines := make([]string, 0, 10) | ||||||
| @ -132,46 +127,21 @@ func (p *playerInfo) updatePlayerInfo(ctx context.Context) error { | |||||||
| 	text := strings.Join(lines, "\n") | 	text := strings.Join(lines, "\n") | ||||||
|  |  | ||||||
| 	if text != p.messageText { | 	if text != p.messageText { | ||||||
|  | 		messStorage := getMessageStorage(ctx) | ||||||
|  | 		err := messStorage.apply(ctx, fmt.Sprintf("playerBoard:%s", p.playerID), 0, func(interface{}) (string, interface{}) { | ||||||
|  | 			return text, nil | ||||||
|  | 		}) | ||||||
|  |  | ||||||
| 		var c tgbotapi.Chattable |  | ||||||
|  |  | ||||||
| 		if p.playerMessageID == 0 { |  | ||||||
| 			mess := tgbotapi.NewMessage(conf.ChatID, text) |  | ||||||
| 			mess.ParseMode = tgbotapi.ModeMarkdown |  | ||||||
|  |  | ||||||
| 			c = mess |  | ||||||
| 		} else { |  | ||||||
| 			mess := tgbotapi.NewEditMessageText(conf.ChatID, p.playerMessageID, text) |  | ||||||
| 			mess.ParseMode = tgbotapi.ModeMarkdown |  | ||||||
|  |  | ||||||
| 			c = mess |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		m, err := botApi.Send(c) |  | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return errors.Wrap(err, "Failed to send message") | 			return err | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		p.playerMessageID = m.MessageID |  | ||||||
| 		p.messageText = text | 		p.messageText = text | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (p *playerInfo) deletePlayerInfo(ctx context.Context) error { |  | ||||||
| 	botApi := getBotApi(ctx) |  | ||||||
| 	conf := getConfig(ctx) |  | ||||||
|  |  | ||||||
| 	mess := tgbotapi.NewDeleteMessage(conf.ChatID, p.playerMessageID) |  | ||||||
| 	_, err := botApi.Send(mess) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return errors.Wrap(err, "Failed to send message") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *playerInfo) setAchievement(ach achievement) { | func (p *playerInfo) setAchievement(ach achievement) { | ||||||
| 	for _, a := range p.achievements { | 	for _, a := range p.achievements { | ||||||
| 		if a == ach { | 		if a == ach { | ||||||
| @ -249,11 +219,13 @@ func (pb *playersBoard) getPlayerBoard(playerID string) *playerInfo { | |||||||
| 	pb.mx.Lock() | 	pb.mx.Lock() | ||||||
| 	defer pb.mx.Unlock() | 	defer pb.mx.Unlock() | ||||||
|  |  | ||||||
| 	pb.players = append(pb.players, &playerInfo{ | 	pi := &playerInfo{ | ||||||
| 		playerID: playerID, | 		playerID: playerID, | ||||||
| 	}) | 	} | ||||||
|  |  | ||||||
| 	return pb.players[len(pb.players)-1] | 	pb.players = append(pb.players, pi) | ||||||
|  |  | ||||||
|  | 	return pi | ||||||
| } | } | ||||||
|  |  | ||||||
| func (pb *playersBoard) updateAchievements(ctx context.Context, ach achievement) error { | func (pb *playersBoard) updateAchievements(ctx context.Context, ach achievement) error { | ||||||
| @ -347,19 +319,3 @@ func (pb *playersBoard) update(ctx context.Context, as ...achievement) error { | |||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (pb *playersBoard) run(ctx context.Context) { |  | ||||||
| 	log := getLogger(ctx) |  | ||||||
|  |  | ||||||
| 	<-ctx.Done() |  | ||||||
|  |  | ||||||
| 	pb.mx.Lock() |  | ||||||
| 	defer pb.mx.Unlock() |  | ||||||
|  |  | ||||||
| 	for _, p := range pb.players { |  | ||||||
| 		err := p.deletePlayerInfo(ctx) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Error(err) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	