приватные каналы
This commit is contained in:
@@ -521,7 +521,7 @@ func (s *PurchaseOptionalDetails) renderInputPrompt(b *bot.Bot, mode bot.RenderM
|
||||
case "cost_value":
|
||||
text += "<b>Ввод стоимости</b>\n\nНапример: <code>15000</code>"
|
||||
if s.CurrentChannel != "" {
|
||||
text += fmt.Sprintf("\n\nКанал: <b>@%s</b>", s.CurrentChannel)
|
||||
text += fmt.Sprintf("\n\nКанал: <b>%s</b>", channelLabelByKey(s.Channels, s.CurrentChannel))
|
||||
}
|
||||
|
||||
typeLabel := s.costTypeLabelForCurrent()
|
||||
@@ -539,12 +539,12 @@ func (s *PurchaseOptionalDetails) renderInputPrompt(b *bot.Bot, mode bot.RenderM
|
||||
case "cost_before_value":
|
||||
text += "<b>Ввод стоимости до торга</b>\n\nНапример: <code>20000</code>"
|
||||
if s.CurrentChannel != "" {
|
||||
text += fmt.Sprintf("\n\nКанал: <b>@%s</b>", s.CurrentChannel)
|
||||
text += fmt.Sprintf("\n\nКанал: <b>%s</b>", channelLabelByKey(s.Channels, s.CurrentChannel))
|
||||
}
|
||||
case "format_select":
|
||||
text = "<i>Страница ввода формата размещения</i>\n\n"
|
||||
if s.CurrentChannel != "" {
|
||||
text += fmt.Sprintf("Канал: <b>@%s</b>\n\n", s.CurrentChannel)
|
||||
text += fmt.Sprintf("Канал: <b>%s</b>\n\n", channelLabelByKey(s.Channels, s.CurrentChannel))
|
||||
}
|
||||
text += "Выберите формат\n\n"
|
||||
keyboard := Keyboard(
|
||||
@@ -707,7 +707,7 @@ func (s *PurchaseOptionalDetails) renderParamChannels(b *bot.Bot, mode bot.Rende
|
||||
// Список каналов с кнопками в зависимости от режима
|
||||
for i := start; i < end; i++ {
|
||||
label := fmt.Sprintf("%d", i+1)
|
||||
channelUsername := s.Channels[i].Username
|
||||
channelKey := channelKey(s.Channels[i])
|
||||
|
||||
if s.ChannelEditMode == "copy" {
|
||||
// Режим копирования
|
||||
@@ -723,7 +723,7 @@ func (s *PurchaseOptionalDetails) renderParamChannels(b *bot.Bot, mode bot.Rende
|
||||
}
|
||||
|
||||
// Кнопка удаления - только если есть данные у канала
|
||||
if s.hasChannelValue(param, channelUsername) {
|
||||
if s.hasChannelValue(param, channelKey) {
|
||||
channelRow = append(channelRow, Button("⌫", fmt.Sprintf("param_clear:%s:%d", param, i)))
|
||||
} else {
|
||||
channelRow = append(channelRow, Button(" ", "noop"))
|
||||
@@ -736,7 +736,7 @@ func (s *PurchaseOptionalDetails) renderParamChannels(b *bot.Bot, mode bot.Rende
|
||||
channelRow = append(channelRow, Button(label, fmt.Sprintf("param_edit:%s:%d", param, i)))
|
||||
|
||||
// Кнопка "Добавить" или "Изменить" в зависимости от наличия данных
|
||||
hasValue := s.hasChannelValue(param, channelUsername)
|
||||
hasValue := s.hasChannelValue(param, channelKey)
|
||||
if hasValue {
|
||||
channelRow = append(channelRow, Button("Изменить", fmt.Sprintf("param_edit:%s:%d", param, i)))
|
||||
} else {
|
||||
@@ -772,8 +772,9 @@ func (s *PurchaseOptionalDetails) renderParamChannels(b *bot.Bot, mode bot.Rende
|
||||
func (s *PurchaseOptionalDetails) renderParamChannelSummary(param string) string {
|
||||
var lines []string
|
||||
for i, ch := range s.Channels {
|
||||
value := s.formatParamValue(param, ch.Username)
|
||||
lines = append(lines, fmt.Sprintf("%d. @%s — %s", i+1, ch.Username, value))
|
||||
channelKey := channelKey(ch)
|
||||
value := s.formatParamValue(param, channelKey)
|
||||
lines = append(lines, fmt.Sprintf("%d. %s — %s", i+1, channelLabel(ch), value))
|
||||
}
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
@@ -788,7 +789,7 @@ func (s *PurchaseOptionalDetails) handleParamCallback(b *bot.Bot, data string) b
|
||||
}
|
||||
if index >= 0 && index < len(s.Channels) {
|
||||
s.CurrentParam = param
|
||||
s.CurrentChannel = s.Channels[index].Username
|
||||
s.CurrentChannel = channelKey(s.Channels[index])
|
||||
s.openChannelEditor(b, param, s.CurrentChannel)
|
||||
return true
|
||||
}
|
||||
@@ -801,8 +802,8 @@ func (s *PurchaseOptionalDetails) handleParamCallback(b *bot.Bot, data string) b
|
||||
return true
|
||||
}
|
||||
if index >= 0 && index < len(s.Channels) {
|
||||
username := s.Channels[index].Username
|
||||
s.copyParamValue(param, username)
|
||||
channelKey := channelKey(s.Channels[index])
|
||||
s.copyParamValue(param, channelKey)
|
||||
s.Enter(b, bot.EditMessage)
|
||||
return true
|
||||
}
|
||||
@@ -815,8 +816,8 @@ func (s *PurchaseOptionalDetails) handleParamCallback(b *bot.Bot, data string) b
|
||||
return true
|
||||
}
|
||||
if index >= 0 && index < len(s.Channels) {
|
||||
username := s.Channels[index].Username
|
||||
s.pasteParamValue(param, username)
|
||||
channelKey := channelKey(s.Channels[index])
|
||||
s.pasteParamValue(param, channelKey)
|
||||
s.Enter(b, bot.EditMessage)
|
||||
return true
|
||||
}
|
||||
@@ -829,8 +830,8 @@ func (s *PurchaseOptionalDetails) handleParamCallback(b *bot.Bot, data string) b
|
||||
return true
|
||||
}
|
||||
if index >= 0 && index < len(s.Channels) {
|
||||
username := s.Channels[index].Username
|
||||
s.clearParamValue(param, username)
|
||||
channelKey := channelKey(s.Channels[index])
|
||||
s.clearParamValue(param, channelKey)
|
||||
s.Enter(b, bot.EditMessage)
|
||||
return true
|
||||
}
|
||||
@@ -902,31 +903,31 @@ func (s *PurchaseOptionalDetails) openCommonEditor(b *bot.Bot, param string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PurchaseOptionalDetails) openChannelEditor(b *bot.Bot, param, username string) {
|
||||
func (s *PurchaseOptionalDetails) openChannelEditor(b *bot.Bot, param, channelKey string) {
|
||||
switch param {
|
||||
case "placement":
|
||||
s.InputMode = "placement_channels"
|
||||
b.SetState(ui2.NewDateTimePicker(ui2.DateTimePickerConfig{
|
||||
Title: "Дата и время размещения",
|
||||
Key: "placement_datetime:" + username,
|
||||
Key: "placement_datetime:" + channelKey,
|
||||
IncludeTime: true,
|
||||
Selected: s.PlacementByChannel[username],
|
||||
Selected: s.PlacementByChannel[channelKey],
|
||||
BackState: s,
|
||||
}), bot.EditMessage)
|
||||
case "cost":
|
||||
s.InputMode = "cost_value"
|
||||
s.ReturnMode = "cost_channels"
|
||||
s.CurrentChannel = username
|
||||
s.CurrentChannel = channelKey
|
||||
s.Enter(b, bot.EditMessage)
|
||||
case "cost_before":
|
||||
s.InputMode = "cost_before_value"
|
||||
s.ReturnMode = "cost_before_channels"
|
||||
s.CurrentChannel = username
|
||||
s.CurrentChannel = channelKey
|
||||
s.Enter(b, bot.EditMessage)
|
||||
case "format":
|
||||
s.InputMode = "format_select"
|
||||
s.ReturnMode = "format_channels"
|
||||
s.CurrentChannel = username
|
||||
s.CurrentChannel = channelKey
|
||||
s.Enter(b, bot.EditMessage)
|
||||
default:
|
||||
s.InputMode = ""
|
||||
@@ -934,12 +935,12 @@ func (s *PurchaseOptionalDetails) openChannelEditor(b *bot.Bot, param, username
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PurchaseOptionalDetails) copyParamValue(param, username string) {
|
||||
func (s *PurchaseOptionalDetails) copyParamValue(param, channelKey string) {
|
||||
switch param {
|
||||
case "placement":
|
||||
s.PlacementCopy = s.PlacementByChannel[username]
|
||||
s.PlacementCopy = s.PlacementByChannel[channelKey]
|
||||
case "cost":
|
||||
entry := s.CostByChannel[username]
|
||||
entry := s.CostByChannel[channelKey]
|
||||
copied := entry
|
||||
if entry.Value == nil {
|
||||
copied.Value = nil
|
||||
@@ -949,9 +950,9 @@ func (s *PurchaseOptionalDetails) copyParamValue(param, username string) {
|
||||
}
|
||||
s.CostCopy = &copied
|
||||
case "cost_before":
|
||||
s.CostBeforeCopy = s.CostBeforeByChannel[username]
|
||||
s.CostBeforeCopy = s.CostBeforeByChannel[channelKey]
|
||||
case "format":
|
||||
if value, ok := s.FormatByChannel[username]; ok {
|
||||
if value, ok := s.FormatByChannel[channelKey]; ok {
|
||||
copied := value
|
||||
s.FormatCopy = &copied
|
||||
} else {
|
||||
@@ -960,18 +961,18 @@ func (s *PurchaseOptionalDetails) copyParamValue(param, username string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PurchaseOptionalDetails) pasteParamValue(param, username string) {
|
||||
func (s *PurchaseOptionalDetails) pasteParamValue(param, channelKey string) {
|
||||
switch param {
|
||||
case "placement":
|
||||
if s.PlacementCopy == nil {
|
||||
s.PlacementByChannel[username] = nil
|
||||
s.PlacementByChannel[channelKey] = nil
|
||||
return
|
||||
}
|
||||
value := s.PlacementCopy.In(ui2.MskLocation)
|
||||
s.PlacementByChannel[username] = &value
|
||||
s.PlacementByChannel[channelKey] = &value
|
||||
case "cost":
|
||||
if s.CostCopy == nil {
|
||||
delete(s.CostByChannel, username)
|
||||
delete(s.CostByChannel, channelKey)
|
||||
return
|
||||
}
|
||||
copied := *s.CostCopy
|
||||
@@ -979,20 +980,20 @@ func (s *PurchaseOptionalDetails) pasteParamValue(param, username string) {
|
||||
value := *copied.Value
|
||||
copied.Value = &value
|
||||
}
|
||||
s.CostByChannel[username] = copied
|
||||
s.CostByChannel[channelKey] = copied
|
||||
case "cost_before":
|
||||
if s.CostBeforeCopy == nil {
|
||||
s.CostBeforeByChannel[username] = nil
|
||||
s.CostBeforeByChannel[channelKey] = nil
|
||||
return
|
||||
}
|
||||
value := *s.CostBeforeCopy
|
||||
s.CostBeforeByChannel[username] = &value
|
||||
s.CostBeforeByChannel[channelKey] = &value
|
||||
case "format":
|
||||
if s.FormatCopy == nil {
|
||||
delete(s.FormatByChannel, username)
|
||||
delete(s.FormatByChannel, channelKey)
|
||||
return
|
||||
}
|
||||
s.FormatByChannel[username] = *s.FormatCopy
|
||||
s.FormatByChannel[channelKey] = *s.FormatCopy
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1000,33 +1001,33 @@ func (s *PurchaseOptionalDetails) applyParamToAll(param string) {
|
||||
switch param {
|
||||
case "placement":
|
||||
for _, ch := range s.Channels {
|
||||
s.pasteParamValue(param, ch.Username)
|
||||
s.pasteParamValue(param, channelKey(ch))
|
||||
}
|
||||
case "cost":
|
||||
for _, ch := range s.Channels {
|
||||
s.pasteParamValue(param, ch.Username)
|
||||
s.pasteParamValue(param, channelKey(ch))
|
||||
}
|
||||
case "cost_before":
|
||||
for _, ch := range s.Channels {
|
||||
s.pasteParamValue(param, ch.Username)
|
||||
s.pasteParamValue(param, channelKey(ch))
|
||||
}
|
||||
case "format":
|
||||
for _, ch := range s.Channels {
|
||||
s.pasteParamValue(param, ch.Username)
|
||||
s.pasteParamValue(param, channelKey(ch))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PurchaseOptionalDetails) clearParamValue(param, username string) {
|
||||
func (s *PurchaseOptionalDetails) clearParamValue(param, channelKey string) {
|
||||
switch param {
|
||||
case "placement":
|
||||
s.PlacementByChannel[username] = nil
|
||||
s.PlacementByChannel[channelKey] = nil
|
||||
case "cost":
|
||||
delete(s.CostByChannel, username)
|
||||
delete(s.CostByChannel, channelKey)
|
||||
case "cost_before":
|
||||
s.CostBeforeByChannel[username] = nil
|
||||
s.CostBeforeByChannel[channelKey] = nil
|
||||
case "format":
|
||||
delete(s.FormatByChannel, username)
|
||||
delete(s.FormatByChannel, channelKey)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1116,7 +1117,10 @@ func (s *PurchaseOptionalDetails) ensureDefaults() {
|
||||
func (s *PurchaseOptionalDetails) syncChannelMaps() {
|
||||
valid := make(map[string]struct{}, len(s.Channels))
|
||||
for _, ch := range s.Channels {
|
||||
valid[ch.Username] = struct{}{}
|
||||
key := channelKey(ch)
|
||||
if key != "" {
|
||||
valid[key] = struct{}{}
|
||||
}
|
||||
}
|
||||
for key := range s.PlacementByChannel {
|
||||
if _, ok := valid[key]; !ok {
|
||||
@@ -1196,17 +1200,17 @@ func (s *PurchaseOptionalDetails) formatFormatDetails() string {
|
||||
return "\n" + s.renderParamChannelSummary("format")
|
||||
}
|
||||
|
||||
func (s *PurchaseOptionalDetails) formatParamValue(param, username string) string {
|
||||
func (s *PurchaseOptionalDetails) formatParamValue(param, channelKey string) string {
|
||||
switch param {
|
||||
case "placement":
|
||||
value := s.PlacementByChannel[username]
|
||||
value := s.PlacementByChannel[channelKey]
|
||||
return s.formatDateTime(value)
|
||||
case "cost":
|
||||
return s.formatCostForChannel(username)
|
||||
return s.formatCostForChannel(channelKey)
|
||||
case "cost_before":
|
||||
return s.formatCostBeforeForChannel(username)
|
||||
return s.formatCostBeforeForChannel(channelKey)
|
||||
case "format":
|
||||
if value, ok := s.FormatByChannel[username]; ok && value != "" {
|
||||
if value, ok := s.FormatByChannel[channelKey]; ok && value != "" {
|
||||
return value
|
||||
}
|
||||
return "—"
|
||||
@@ -1215,8 +1219,8 @@ func (s *PurchaseOptionalDetails) formatParamValue(param, username string) strin
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PurchaseOptionalDetails) formatCostForChannel(username string) string {
|
||||
entry, ok := s.CostByChannel[username]
|
||||
func (s *PurchaseOptionalDetails) formatCostForChannel(channelKey string) string {
|
||||
entry, ok := s.CostByChannel[channelKey]
|
||||
if !ok || entry.Value == nil {
|
||||
return "—"
|
||||
}
|
||||
@@ -1224,8 +1228,8 @@ func (s *PurchaseOptionalDetails) formatCostForChannel(username string) string {
|
||||
return fmt.Sprintf("%s %.0f₽", label, *entry.Value)
|
||||
}
|
||||
|
||||
func (s *PurchaseOptionalDetails) formatCostBeforeForChannel(username string) string {
|
||||
value, ok := s.CostBeforeByChannel[username]
|
||||
func (s *PurchaseOptionalDetails) formatCostBeforeForChannel(channelKey string) string {
|
||||
value, ok := s.CostBeforeByChannel[channelKey]
|
||||
if !ok || value == nil {
|
||||
return "—"
|
||||
}
|
||||
@@ -1247,19 +1251,19 @@ func (s *PurchaseOptionalDetails) hasCopyBuffer(param string) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PurchaseOptionalDetails) hasChannelValue(param, username string) bool {
|
||||
func (s *PurchaseOptionalDetails) hasChannelValue(param, channelKey string) bool {
|
||||
switch param {
|
||||
case "placement":
|
||||
value, ok := s.PlacementByChannel[username]
|
||||
value, ok := s.PlacementByChannel[channelKey]
|
||||
return ok && value != nil
|
||||
case "cost":
|
||||
entry, ok := s.CostByChannel[username]
|
||||
entry, ok := s.CostByChannel[channelKey]
|
||||
return ok && entry.Value != nil
|
||||
case "cost_before":
|
||||
value, ok := s.CostBeforeByChannel[username]
|
||||
value, ok := s.CostBeforeByChannel[channelKey]
|
||||
return ok && value != nil
|
||||
case "format":
|
||||
value, ok := s.FormatByChannel[username]
|
||||
value, ok := s.FormatByChannel[channelKey]
|
||||
return ok && value != ""
|
||||
default:
|
||||
return false
|
||||
@@ -1412,14 +1416,14 @@ func (s *PurchaseOptionalDetails) createPurchase(b *bot.Bot, jwt string) {
|
||||
|
||||
apiChannels := make([]backend.CreatePlacementChannelInput, 0, len(s.Channels))
|
||||
for _, ch := range s.Channels {
|
||||
channelDetails := s.buildChannelDetails(ch.Username)
|
||||
channelDetails := s.buildChannelDetails(channelKey(ch))
|
||||
if channelDetails != nil && s.isChannelDetailsEmpty(channelDetails) {
|
||||
channelDetails = nil
|
||||
}
|
||||
apiChannels = append(apiChannels, backend.CreatePlacementChannelInput{
|
||||
Username: ch.Username,
|
||||
Comment: ch.Comment,
|
||||
Details: channelDetails,
|
||||
ChannelID: ch.ChannelID,
|
||||
Comment: ch.Comment,
|
||||
Details: channelDetails,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1495,29 +1499,29 @@ func (s *PurchaseOptionalDetails) buildPlacementDetails(placementType *string) *
|
||||
return details
|
||||
}
|
||||
|
||||
func (s *PurchaseOptionalDetails) buildChannelDetails(username string) *backend.PlacementDetails {
|
||||
func (s *PurchaseOptionalDetails) buildChannelDetails(channelKey string) *backend.PlacementDetails {
|
||||
if len(s.Channels) <= 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
details := &backend.PlacementDetails{}
|
||||
if s.PlacementMode == "per_channel" {
|
||||
if value, ok := s.PlacementByChannel[username]; ok && value != nil {
|
||||
if value, ok := s.PlacementByChannel[channelKey]; ok && value != nil {
|
||||
formatted := value.Format(time.RFC3339)
|
||||
details.PlacementAt = &formatted
|
||||
}
|
||||
}
|
||||
if s.CostMode == "per_channel" {
|
||||
entry := s.CostByChannel[username]
|
||||
entry := s.CostByChannel[channelKey]
|
||||
details.Cost = s.buildCostInfo(entry.Type, entry.Value)
|
||||
}
|
||||
if s.CostBeforeMode == "per_channel" {
|
||||
if value, ok := s.CostBeforeByChannel[username]; ok && value != nil {
|
||||
if value, ok := s.CostBeforeByChannel[channelKey]; ok && value != nil {
|
||||
details.CostBeforeBargain = value
|
||||
}
|
||||
}
|
||||
if s.FormatMode == "per_channel" {
|
||||
if value, ok := s.FormatByChannel[username]; ok && value != "" {
|
||||
if value, ok := s.FormatByChannel[channelKey]; ok && value != "" {
|
||||
details.Format = &value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,10 @@ import (
|
||||
)
|
||||
|
||||
type PurchaseChannelInput struct {
|
||||
ChannelID string
|
||||
Username string
|
||||
Title string
|
||||
InviteLink string
|
||||
PlannedCost *float64
|
||||
Comment *string
|
||||
}
|
||||
@@ -48,15 +51,15 @@ func (s *SelectChannelsForPurchase) renderChannelSelection(b *bot.Bot, mode bot.
|
||||
if len(s.Channels) == 0 {
|
||||
text += `Добавьте каналы для размещения рекламы
|
||||
|
||||
Отправьте username канала (без @)
|
||||
Например: <code>channel_name</code>
|
||||
Отправьте username (без @) или invite link приватного канала
|
||||
Например: <code>channel_name</code> или <code>https://t.me/+abcdef</code>
|
||||
|
||||
После добавления всех каналов нажмите <b>Далее</b>`
|
||||
} else {
|
||||
text += fmt.Sprintf("Добавлено каналов: <b>%d</b>\n\n", len(s.Channels))
|
||||
|
||||
for i, ch := range s.Channels {
|
||||
channelText := fmt.Sprintf(" %d. @%s", i+1, ch.Username)
|
||||
channelText := fmt.Sprintf(" %d. %s", i+1, channelLabel(ch))
|
||||
if ch.PlannedCost != nil {
|
||||
channelText += fmt.Sprintf(" — %.0f₽", *ch.PlannedCost)
|
||||
}
|
||||
@@ -73,9 +76,9 @@ func (s *SelectChannelsForPurchase) renderChannelSelection(b *bot.Bot, mode bot.
|
||||
var slots []echotron.InlineKeyboardButton
|
||||
for i := start; i < end; i++ {
|
||||
ch := s.Channels[i]
|
||||
buttonText := fmt.Sprintf("@%s", ch.Username)
|
||||
buttonText := channelLabel(ch)
|
||||
if ch.PlannedCost != nil {
|
||||
buttonText = fmt.Sprintf("@%s — %.0f₽", ch.Username, *ch.PlannedCost)
|
||||
buttonText = fmt.Sprintf("%s — %.0f₽", channelLabel(ch), *ch.PlannedCost)
|
||||
}
|
||||
slots = append(slots, Button(buttonText, fmt.Sprintf("remove_channel:%d", i)))
|
||||
}
|
||||
@@ -161,7 +164,7 @@ func (s *SelectChannelsForPurchase) HandleCallback(b *bot.Bot, u *echotron.Updat
|
||||
return
|
||||
}
|
||||
|
||||
// Проверяем существование каналов через API
|
||||
// Создаем/обновляем каналы в БД через API
|
||||
jwt := b.Session.JWT
|
||||
if jwt == "" {
|
||||
log.Error().Msg("JWT is empty in session")
|
||||
@@ -169,11 +172,11 @@ func (s *SelectChannelsForPurchase) HandleCallback(b *bot.Bot, u *echotron.Updat
|
||||
return
|
||||
}
|
||||
|
||||
notFoundChannels := s.validateChannels(b, jwt)
|
||||
if len(notFoundChannels) > 0 {
|
||||
text := "❌ Следующие каналы не найдены:\n\n"
|
||||
for _, username := range notFoundChannels {
|
||||
text += fmt.Sprintf("• @%s\n", username)
|
||||
failed := s.resolveChannels(b, jwt)
|
||||
if len(failed) > 0 {
|
||||
text := "❌ Не удалось добавить каналы:\n\n"
|
||||
for _, label := range failed {
|
||||
text += fmt.Sprintf("• %s\n", label)
|
||||
}
|
||||
text += "\nУдалите их из списка и попробуйте снова"
|
||||
|
||||
@@ -242,9 +245,9 @@ func (s *SelectChannelsForPurchase) createPurchase(b *bot.Bot, jwt string) {
|
||||
}
|
||||
}
|
||||
apiChannels = append(apiChannels, backend.CreatePlacementChannelInput{
|
||||
Username: ch.Username,
|
||||
Comment: ch.Comment,
|
||||
Details: details,
|
||||
ChannelID: ch.ChannelID,
|
||||
Comment: ch.Comment,
|
||||
Details: details,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -294,22 +297,32 @@ func (s *SelectChannelsForPurchase) HandleMessage(b *bot.Bot, u *echotron.Update
|
||||
added := 0
|
||||
var invalid []string
|
||||
for _, token := range tokens {
|
||||
username := strings.TrimSpace(token)
|
||||
username = strings.TrimPrefix(username, "@")
|
||||
username = strings.Trim(username, "@")
|
||||
username = strings.Trim(username, ",;")
|
||||
|
||||
if username == "" {
|
||||
entry := strings.TrimSpace(token)
|
||||
entry = strings.Trim(entry, ",;")
|
||||
if entry == "" {
|
||||
continue
|
||||
}
|
||||
if !channelUsernameRe.MatchString(username) {
|
||||
invalid = append(invalid, username)
|
||||
continue
|
||||
|
||||
var username string
|
||||
var inviteLink string
|
||||
if isInviteLink(entry) {
|
||||
inviteLink = entry
|
||||
} else {
|
||||
username = strings.TrimPrefix(entry, "@")
|
||||
username = strings.Trim(username, "@")
|
||||
if !channelUsernameRe.MatchString(username) {
|
||||
invalid = append(invalid, entry)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
exists := false
|
||||
for _, ch := range s.Channels {
|
||||
if ch.Username == username {
|
||||
if inviteLink != "" && ch.InviteLink == inviteLink {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
if username != "" && ch.Username == username {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
@@ -319,7 +332,8 @@ func (s *SelectChannelsForPurchase) HandleMessage(b *bot.Bot, u *echotron.Update
|
||||
}
|
||||
|
||||
s.Channels = append(s.Channels, PurchaseChannelInput{
|
||||
Username: username,
|
||||
Username: username,
|
||||
InviteLink: inviteLink,
|
||||
})
|
||||
added++
|
||||
}
|
||||
@@ -336,37 +350,48 @@ func (s *SelectChannelsForPurchase) HandleMessage(b *bot.Bot, u *echotron.Update
|
||||
s.Enter(b, bot.NewMessage)
|
||||
}
|
||||
|
||||
func (s *SelectChannelsForPurchase) validateChannels(b *bot.Bot, jwt string) []string {
|
||||
var notFound []string
|
||||
|
||||
func (s *SelectChannelsForPurchase) resolveChannels(b *bot.Bot, jwt string) []string {
|
||||
inputs := make([]backend.CreateChannelInput, 0, len(s.Channels))
|
||||
for _, ch := range s.Channels {
|
||||
channels, err := b.Backend.SearchChannels(
|
||||
context.Background(),
|
||||
jwt,
|
||||
ch.Username,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("username", ch.Username).Msg("Failed to search channel")
|
||||
notFound = append(notFound, ch.Username)
|
||||
continue
|
||||
}
|
||||
|
||||
// Проверяем что нашли хотя бы один канал с точным совпадением username
|
||||
found := false
|
||||
for _, channel := range channels {
|
||||
if channel.Username != nil && *channel.Username == ch.Username {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
notFound = append(notFound, ch.Username)
|
||||
if ch.InviteLink != "" {
|
||||
link := ch.InviteLink
|
||||
inputs = append(inputs, backend.CreateChannelInput{InviteLink: &link})
|
||||
} else {
|
||||
username := ch.Username
|
||||
inputs = append(inputs, backend.CreateChannelInput{Username: &username})
|
||||
}
|
||||
}
|
||||
|
||||
return notFound
|
||||
resp, err := b.Backend.CreateChannels(context.Background(), jwt, backend.CreateChannelsInput{
|
||||
Channels: inputs,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to create channels")
|
||||
return []string{"ошибка сервера"}
|
||||
}
|
||||
|
||||
var failed []string
|
||||
for _, result := range resp.Results {
|
||||
if result.Status == "failed" || result.Channel == nil || result.Channel.ID == "" {
|
||||
if result.Index >= 0 && result.Index < len(s.Channels) {
|
||||
failed = append(failed, channelLabel(s.Channels[result.Index]))
|
||||
}
|
||||
continue
|
||||
}
|
||||
if result.Index < 0 || result.Index >= len(s.Channels) {
|
||||
continue
|
||||
}
|
||||
ch := &s.Channels[result.Index]
|
||||
ch.ChannelID = result.Channel.ID
|
||||
if result.Channel.Username != nil {
|
||||
ch.Username = *result.Channel.Username
|
||||
}
|
||||
if result.Channel.Title != nil {
|
||||
ch.Title = *result.Channel.Title
|
||||
}
|
||||
}
|
||||
|
||||
return failed
|
||||
}
|
||||
|
||||
func (s *SelectChannelsForPurchase) Handle(b *bot.Bot, u *echotron.Update) {
|
||||
@@ -380,3 +405,52 @@ func (s *SelectChannelsForPurchase) Handle(b *bot.Bot, u *echotron.Update) {
|
||||
}
|
||||
|
||||
func (s *SelectChannelsForPurchase) Exit() {}
|
||||
|
||||
func channelLabel(ch PurchaseChannelInput) string {
|
||||
if ch.Username != "" {
|
||||
return "@" + ch.Username
|
||||
}
|
||||
if ch.Title != "" {
|
||||
return ch.Title
|
||||
}
|
||||
if ch.InviteLink != "" {
|
||||
return "invite link"
|
||||
}
|
||||
if ch.ChannelID != "" {
|
||||
return ch.ChannelID
|
||||
}
|
||||
return "канал"
|
||||
}
|
||||
|
||||
func channelKey(ch PurchaseChannelInput) string {
|
||||
if ch.ChannelID != "" {
|
||||
return ch.ChannelID
|
||||
}
|
||||
if ch.Username != "" {
|
||||
return ch.Username
|
||||
}
|
||||
if ch.InviteLink != "" {
|
||||
return ch.InviteLink
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func channelLabelByKey(channels []PurchaseChannelInput, key string) string {
|
||||
for _, ch := range channels {
|
||||
if channelKey(ch) == key {
|
||||
return channelLabel(ch)
|
||||
}
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func isInviteLink(value string) bool {
|
||||
value = strings.TrimSpace(value)
|
||||
if strings.Contains(value, "t.me/") || strings.Contains(value, "telegram.me/") {
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(value, "tg://") || strings.HasPrefix(value, "tg:") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user