Compare commits

...

29 Commits
main ... dev

Author SHA1 Message Date
ef074b98e5 added func for show results 2024-08-15 14:19:06 +03:00
72ea3c83a4 added worked with memory and allocations 2024-08-15 13:47:35 +03:00
d07031c242 added test 2024-08-15 11:52:20 +03:00
c6d1e79360 fix if n != 0 run(n) with this n 2024-08-14 23:49:52 +03:00
af9aca41b4 added tg bot handle 2024-08-13 18:36:47 +03:00
5b079a5872 added some workflow tests 2024-08-12 17:59:29 +03:00
20f24be349 added test for run_benchmark func 2024-08-12 16:00:04 +03:00
36e6a0f986 - 2024-08-12 14:21:43 +03:00
6f0d84aa87 remember n if sent 2024-08-12 14:03:38 +03:00
a4d4cee6cd delete v benchmark code, added separate methods and our struct 2024-08-12 13:26:30 +03:00
f7be57f9dd moved the benchmark from v to our file, added a constructor to initialize our benchmark 2024-08-12 11:57:35 +03:00
57491651d2 added 2 test for predict_n 2024-08-11 17:51:51 +03:00
5ee7a22486 added bench file to our repo 2024-08-11 17:16:10 +03:00
cfd1bb66bd added init function for prepare dataset out bench in v code, but it anti pattern 2024-08-09 17:44:20 +03:00
6e368518c5 added init function for prepare dataset out bench 2024-08-09 17:15:43 +03:00
3e4851555d added fix start state and move seed to main file 2024-08-09 15:40:00 +03:00
5f73c5ab5a added our bench to main 2024-08-08 23:40:21 +03:00
e730068834 rework logic, parse fromm file 2024-08-08 22:57:58 +03:00
7a43213aa4 added simply readme for go2v 2024-08-05 15:10:55 +03:00
d9db1b406a commented sleep 2024-08-05 14:52:36 +03:00
2d295bb384 added test result at time for v code 2024-08-05 14:30:20 +03:00
20b6d055bb - 2024-08-02 16:35:37 +03:00
545366564f added bench and unsafe for strconv 2024-08-02 16:03:46 +03:00
aee5d92d8d added bench for go and save results 2024-08-02 14:26:08 +03:00
756272d064 remove contructirs from v 2024-08-02 14:19:49 +03:00
dfa0f2daff add waitgroup and thred call in function 2024-08-02 14:10:30 +03:00
124ee61e1d added worked code on v lang need check with documentation and do with corutines 2024-08-01 17:23:54 +03:00
55f710cbbb added base simply logic for quiz with simulation user choice 2024-07-30 14:29:28 +03:00
9fe660aa71 added base simply logic for quiz with simulation user choice 2024-07-30 14:28:59 +03:00
21 changed files with 8159 additions and 0 deletions

1
.gitignore vendored Normal file

@ -0,0 +1 @@
.idea/

@ -1,6 +1,25 @@
# telegram
`go2v` — утилита для транслирования кода на Go в V. В текущий момент доступна только старая версия утилиты.
Для установки `go2v`, нужно клонировать репозиторий в данный момент с ветки `old_implementation` и запустить команду `v run .` для компиляции файлов:
```sh
git clone https://github.com/vlang/go2v.git -b old_implementation
cd go2v
v run .
```
После установки утилиты, можно использовать go2v для трансляции Go-файлов в V:
```sh
v run . path/to/file.go
```
Транслированный файл будет создан в том же каталоге с именем v.
Может потребоваться проверка синтаксиса, так как транслирование может содержать ошибки.
Дополнительную информацию и документацию можно найти в репозитории [go2v на GitHub](https://github.com/vlang/go2v/tree/old_implementation).
## Getting started

18
client/client.go Normal file

@ -0,0 +1,18 @@
package client
import "fmt"
type Client struct {
Messages []string
}
func NewClient() *Client {
return &Client{
Messages: []string{},
}
}
func (c *Client) SendMessage(msg string) {
c.Messages = append(c.Messages, msg)
fmt.Println("saved message: ", msg)
}

11
client/client.v Normal file

@ -0,0 +1,11 @@
module client
pub struct Client {
pub mut:
messages []string
}
pub fn (mut c Client) send_message(msg string) {
c.messages << msg
//println('saved message: ${msg}')
}

3
go.mod Normal file

@ -0,0 +1,3 @@
module penahub.gitlab.yandexcloud.net/backend/quiz/telegram
go 1.22.5

71
main.go Normal file

@ -0,0 +1,71 @@
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"math/rand"
"os"
"penahub.gitlab.yandexcloud.net/backend/quiz/telegram/client"
"penahub.gitlab.yandexcloud.net/backend/quiz/telegram/models"
"penahub.gitlab.yandexcloud.net/backend/quiz/telegram/respondent"
stateManager "penahub.gitlab.yandexcloud.net/backend/quiz/telegram/state_manager"
"time"
)
var dataSetMap map[string]models.Question
func init() {
f, err := os.Open("treedata.json")
if err != nil {
log.Fatalf("err open json file: %v", err)
}
defer f.Close()
values, err := io.ReadAll(f)
if err != nil {
log.Fatalf("err read json file: %v", err)
}
var dataSet []models.Question
err = json.Unmarshal(values, &dataSet)
if err != nil {
log.Fatalf("err unmarshall json: %v", err)
}
dataSetMap = make(map[string]models.Question)
for _, data := range dataSet {
dataSetMap[data.Title] = data
}
}
func main() {
rand.Seed(time.Now().UnixNano())
mockClient := client.NewClient()
mockStateManager := stateManager.NewStateManager(stateManager.Deps{
Questions: dataSetMap,
State: "q00",
Client: mockClient,
})
mockRespondent := respondent.NewRespondent(mockStateManager)
mockStateManager.SendQuestion()
for {
if _, ok := dataSetMap[mockStateManager.State]; !ok {
fmt.Println("state not found")
break
}
mockRespondent.Respond("q00")
fmt.Println("respond")
}
for number, msg := range mockClient.Messages {
fmt.Println(fmt.Sprintf("message %d: %s", number, msg))
}
//}
fmt.Println("Complete")
}

78
main.v Normal file

@ -0,0 +1,78 @@
module main
import json
import log
import os
import client
import models
import respondent
import state_manager
import our_benchmark
import tg_handle
import time
//v -enable-globals run main.v
__global (
data_set_map map[string]models.Question
)
fn init() {
f := os.read_file('treedata.json') or {
log.error('err read json file: $err')
return
}
data_set := json.decode([]models.Question, f) or {
log.error('err unmarshall json: $err')
return
}
for data in data_set {
data_set_map[data.title] = data
}
}
fn main() {
mut b := our_benchmark.new_custom_benchmark(test_main,0,0,false) or {
eprintln('Error creating benchmark: $err')
return
}
b.run_benchmark()
// mut benchmarks := our_benchmark.InternalBenchmark{
// name: 'test_main',
// f: test_main,
// }
// our_benchmark.run_benchmarks(benchmarks)
}
fn test_main()! {
mut mock_client := &client.Client{messages: []string{}}
mut mock_state_manager := &state_manager.StateManager{
questions: data_set_map
state: "q00"
client: mock_client
}
// tg_handle.new_tg_bot("6712573453:AAFqTOsgwe_j48ZQ1GzWKQDT5Nwr-SAWjz8")
mut mock_respondent := respondent.Respondent{manager: mock_state_manager}
mock_state_manager.send_question()
for {
if mock_state_manager.state !in data_set_map {
//println('state not found')
break
}
mock_respondent.respond("q00")
//println('respond')
}
// for number, msg in mock_client.messages {
// println('message $number: $msg')
// }
//println('Complete')
// time.sleep(10 * time.minute)
}

41
main_test.go Normal file

@ -0,0 +1,41 @@
package main
import (
"fmt"
"math/rand"
"penahub.gitlab.yandexcloud.net/backend/quiz/telegram/client"
"penahub.gitlab.yandexcloud.net/backend/quiz/telegram/respondent"
stateManager "penahub.gitlab.yandexcloud.net/backend/quiz/telegram/state_manager"
"testing"
"time"
)
func BenchmarkMain(b *testing.B) {
for n := 0; n < b.N; n++ {
rand.Seed(time.Now().UnixNano())
mockClient := client.NewClient()
mockStateManager := stateManager.NewStateManager(stateManager.Deps{
Questions: dataSetMap,
State: "q00",
Client: mockClient,
})
mockRespondent := respondent.NewRespondent(mockStateManager)
mockStateManager.SendQuestion()
for {
if _, ok := dataSetMap[mockStateManager.State]; !ok {
fmt.Println("state not found")
break
}
mockRespondent.Respond("q00")
fmt.Println("respond")
}
for number, msg := range mockClient.Messages {
fmt.Println(fmt.Sprintf("message %d: %s", number, msg))
}
fmt.Println("Complete")
}
}

37
main_test.v Normal file

@ -0,0 +1,37 @@
module main
import benchmark
import models
import respondent
import state_manager
import client
fn test_main(){
mut bench := benchmark.new_benchmark()
for i := 0; i < models.data_set.len; i++ {
bench.step()
mut mock_client := &client.Client{messages: []string{}}
mut mock_state_manager := &state_manager.StateManager{
questions: models.data_set[i]
state: 'start'
client: mock_client
}
mut mock_respondent := respondent.Respondent{manager: mock_state_manager}
mock_state_manager.send_question()
for mock_state_manager.state != 'end' {
mock_respondent.respond()
println('respond')
}
for number, msg_1 in mock_client.messages {
println(' ${'out'} ${i} ${msg_1} ${number}')
}
bench.ok()
}
bench.stop()
// выводит время
bench.measure("test_main")
println(bench.total_message('remarks about the our_benchmark'))
}

12
models/models.go Normal file

@ -0,0 +1,12 @@
package models
type Question struct {
Title string
Description string
Buttons []Button
}
type Button struct {
Text string
State string
}

16
models/models.v Normal file

@ -0,0 +1,16 @@
module models
// структуры объявляются в принципе как в гошке, заисключением того что видимо взято из С происходит деление на публичные и приватные
// поэтому для того чтобы реализовать инкапсуляцию, если указать ключевое слово pub - будет все доступно
pub struct Button {
pub mut:
text string @[json: 'Text']
state string @[json: 'State']
}
pub struct Question {
pub mut:
title string @[json: 'Title']
description string @[json: 'Description']
buttons []Button @[json: 'Buttons']
}

51
models/variants.go Normal file

@ -0,0 +1,51 @@
package models
var DataSet = []map[string]Question{
{
"start": {
Title: "Start question",
Description: "This first question, start",
Buttons: []Button{
{"start", "q1"},
},
},
"q1": {
Title: "Question 1",
Description: "This question 1 after start question",
Buttons: []Button{
{"q2", "q2"},
{"q3", "q3"},
{"q4", "q4"},
},
},
"q2": {
Title: "Question 2",
Description: "This question 2 after question 1",
Buttons: []Button{
{"Go end", "end"},
{"Need end", "end"},
},
},
"q3": {
Title: "Question 3",
Description: "This question 3 after question 1",
Buttons: []Button{
{"Want end", "end"},
{"Go dota 2", "end"},
},
},
"q4": {
Title: "Question 4",
Description: "This question 4 after question 1",
Buttons: []Button{
{"Run end", "end"},
{"Swimming to end", "end"},
},
},
"end": {
Title: "Last question",
Description: "This is last question",
Buttons: []Button{},
},
},
}

81
models/variants.v Normal file

@ -0,0 +1,81 @@
module models
pub const data_set = [
{
'end': Question{
title: 'Last question'
description: 'This is last question'
buttons: []
}
'q1': Question{
title: 'Question 1'
description: 'This question 1 after start question'
buttons: [
Button{
text: 'q2'
state: 'q2'
},
Button{
text: 'q3'
state: 'q3'
},
Button{
text: 'q4'
state: 'q4'
},
]
}
'q2': Question{
title: 'Question 2'
description: 'This question 2 after question 1'
buttons: [
Button{
text: 'Go end'
state: 'end'
},
Button{
text: 'Need end'
state: 'end'
},
]
}
'q3': Question{
title: 'Question 3'
description: 'This question 3 after question 1'
buttons: [
Button{
text: 'Want end'
state: 'end'
},
Button{
text: 'Go dota 2'
state: 'end'
},
]
}
'q4': Question{
title: 'Question 4'
description: 'This question 4 after question 1'
buttons: [
Button{
text: 'Run end'
state: 'end'
},
Button{
text: 'Swimming to end'
state: 'end'
},
]
}
'start': Question{
title: 'Start question'
description: 'This first question, start'
buttons: [
Button{
text: 'start'
state: 'q1'
},
]
}
},
]

175
our_benchmark/benchmark.v Normal file

@ -0,0 +1,175 @@
module our_benchmark
import time
import math
@[noinit]
pub struct CustomBenchmark {
pub mut:
n i64 // Количество итераций
bench_func ?fn()! // функция которая тестируется, не может быть пустой
bench_time time.Duration // время выполнения бенчмарка дефолтно 1 секунда, проверка на то что не может быть меньше секунды?
is_parallel bool // флаг паралельного запуска, дефолтно 0
benchmark_result BenchmarkResult // сюда складываются результаты бенчмарка
timer_on bool
start_time time.Time
duration time.Duration
failed bool
start_memory usize
start_allocs usize
}
struct BenchmarkResult {
pub mut:
n i64 // Количество итераций
t time.Duration // Общее затраченное время
mem usize // память
allocs usize // аллокации
}
pub fn new_custom_benchmark (bench_func fn()!,n i64,duration time.Duration,is_parallel bool) !CustomBenchmark{
if bench_func == voidptr(0) {
return error('Benchmark function cannot be empty')
}
mut check_duration := duration
if duration == 0 { check_duration = time.second }
return CustomBenchmark{
n:n
bench_func: bench_func
bench_time: check_duration
is_parallel: is_parallel
}
}
pub fn (mut b CustomBenchmark) run_benchmark(){
mut n := b.n // нужно для того чтобы если нам передали n о после прогрева его оставить
b.run_n(1)
// тут проверяем если n != 0 это проверка на то что не передали, по дефолту будет всегда 1 n
if n != 0{
b.n =n
b.run_n(n)
}else {
for !b.failed && b.duration < b.bench_time && n < 1000000000 {
// предсказываем количество итераций, исходя из предыдущих результатов
n = b.predict_n()
// выполняем бенчмарк n раз
b.run_n(n)
}
}
b.benchmark_result.n = b.n
b.benchmark_result.t = b.duration
b.benchmark_result.show()
}
// функция run_n выполняет бенчмарк указанное количество раз (n итераций)
// обеспечивает синхронизацию, управляет временем выполнения и обрабатывает ошибки
fn (mut b CustomBenchmark) run_n(n i64) {
// очищаем память для минимизации влияния сборщика мусора на результаты бенчмарка
gc_collect()
// устанавливаем количество итераций
b.n = n
// сбрасываем и запускаем таймер для измерения времени выполнения
b.reset_timer()
b.start_timer()
// извлекаем функцию бенчмарка
mut f := b.bench_func or {
return
}
// выполняем бенчмарк n раз в цикле
for i := i64(0); i < n; i++ {
f() or {
// если во время выполнения произошла ошибка, устанавливаем флаг ошибки и выводим сообщение
b.failed = true
eprintln('Error: $err')
return
}
}
// останавливаем таймер после выполнения всех итераций
b.stop_timer()
}
// predict_n предсказывает количество итераций для следующего запуска бенчмарка,
// основываясь на предыдущих результатах выполнения
fn (mut b CustomBenchmark) predict_n() i64 {
mut goal_ns := b.bench_time.nanoseconds() // Не меняем, поэтому берем из ресивера
prev_iters := b.n // Берем последний n который был из b.n так как в run_n мы его записываем
mut prev_ns := b.duration.nanoseconds()
if prev_ns <= 0 {
prev_ns = 1
}
mut n := goal_ns *prev_iters
n = n / prev_ns
n += n / 5
n = math.min(n, 100 * b.n)
n = math.max(n, b.n + 1)
n = math.min(n, 1000000000)
return n
}
fn (mut b CustomBenchmark) reset_timer() {
if b.timer_on {
b.start_time = time.now()
b.start_memory = gc_memory_use()
b.start_allocs = gc_heap_usage().bytes_since_gc
}
b.duration = 0
b.benchmark_result.mem = 0
b.benchmark_result.allocs = 0
}
fn (mut b CustomBenchmark) start_timer() {
if !b.timer_on{
b.start_time = time.now()
b.start_memory = gc_memory_use()
b.start_allocs = gc_heap_usage().bytes_since_gc
b.timer_on = true
}
}
fn (mut b CustomBenchmark) stop_timer() {
if b.timer_on{
b.duration += time.since(b.start_time)
b.benchmark_result.mem +=gc_memory_use() - b.start_memory
b.benchmark_result.allocs += gc_heap_usage().bytes_since_gc - b.start_allocs
b.timer_on = false
}
}
fn (r BenchmarkResult) ns_per_op() i64 {
if r.n <= 0 {
return 0
}
return r.t.nanoseconds() / i64(r.n)
}
fn (r BenchmarkResult) allocs_per_op() i64 {
if r.n <= 0 {
return 0
}
return i64(r.allocs) / i64(r.n)
}
fn (r BenchmarkResult) alloced_bytes_per_op() i64 {
if r.n <= 0 {
return 0
}
return i64(r.mem) / i64(r.n)
}
fn (r BenchmarkResult) show() {
println('Iterations: ${r.n}')
println('Total Duration: ${r.t}')
println('ns/op: ${r.ns_per_op()}')
println('B/op: ${r.alloced_bytes_per_op()}')
println('allocs/op: ${r.allocs_per_op()}')
}

@ -0,0 +1,179 @@
module our_benchmark
import time
// if n == 0, n predict == 1
fn test_predict_n_zero() {
mut b := CustomBenchmark{
n: 0,
duration: 0,
bench_time: time.second,
}
expected := 1
println(b.predict_n())
assert b.predict_n() == expected
}
// n can't be more 1000000000
fn test_predict_n_limit() {
mut b := CustomBenchmark{
n: 10000000000,
duration: 0,
bench_time: time.second,
}
expected := 1000000000
assert b.predict_n() == expected
}
// если произошла ошибка в бенч функции то флаг failed должен быть помечен в тру, n = 1
fn test_fn_with_error() {
f := fn() ! {
return error('error')
}
mut bench := our_benchmark.new_custom_benchmark(f, 0, 0, false)or {
eprintln('Error creating benchmark: $err')
return
}
bench.run_benchmark()
assert bench.failed == true
assert bench.benchmark_result.n == 1
}
fn test_n_must_be_over_1(){
f := fn() ! {
mut i := 0
i++
}
mut bench := our_benchmark.new_custom_benchmark(f, 0, 0, false)or {
eprintln('Error creating benchmark: $err')
return
}
bench.run_benchmark()
assert bench.benchmark_result.n > 1
}
fn test_n(){
f := fn() ! {
mut i := 0
i++
}
mut bench := our_benchmark.new_custom_benchmark(f, 1000, 0, false)or {
eprintln('Error creating benchmark: $err')
return
}
bench.run_benchmark()
assert bench.benchmark_result.n == 1000
}
// n == 2 так как прогрев = 1 итерация + 1 итерация == 2
fn test_max_bench_time(){
f := fn() ! {
time.sleep(500* time.millisecond)
}
mut bench := our_benchmark.new_custom_benchmark(f, 0, 0, false)or {
eprintln('Error creating benchmark: $err')
return
}
bench.run_benchmark()
assert bench.benchmark_result.n == 2
assert bench.benchmark_result.t >= time.second
}
// проверка на то что n обновляется корректно после run_n
fn test_n_updated_correct() {
mut b := CustomBenchmark{
n: 1,
bench_time: time.second,
bench_func: fn () ! {
mut i := 0
i++
},
}
b.run_n(100)
assert b.n == 100
b.run_n(1)
assert b.n == 1
}
// проверка на то как вообще работает
fn test_performance() {
scheduler := [func_1,func_2,func_3]
expected := [false,false,false]
mut actual := []bool{}
for i in scheduler{
mut bench := our_benchmark.new_custom_benchmark(i, 0, 0, false)or {
eprintln('Error creating benchmark: $err')
return
}
bench.run_benchmark()
actual << bench.failed
}
assert expected.len == actual.len
for i:=0;i<expected.len;i++{
assert expected[i] == actual[i]
}
}
fn func_1() !{
mut arr := []int{}
appender(mut arr)
assert arr.len == 10
}
fn appender(mut arr []int){
if arr.len==10{
return
}
arr << 1
appender(mut arr)
}
fn func_2() !{
target := 2
arr := [1,2,3,4,5,6,7,8,9,10]
mut left := 0
mut right := arr.len-1
for left<=right{
mid :=left+(right-left)/2
if arr[mid] == target{
return
}
if arr[mid]<target{
left = mid + 1
}
if arr[mid]>target{
right = mid - 1
}
}
return
}
fn func_3() !{
mut arr := [10,2,13,4,5,16,7,1,9,20]
for i :=0; i<arr.len-1;i++{
for j:=0;j<arr.len-i-1;j++{
if arr[j] > arr[j+1]{
arr[j],arr[j+1]=arr[j+1],arr[j]
}
}
}
}

33
respondent/respondent.go Normal file

@ -0,0 +1,33 @@
package respondent
import (
"fmt"
"math/rand"
"penahub.gitlab.yandexcloud.net/backend/quiz/telegram/models"
"penahub.gitlab.yandexcloud.net/backend/quiz/telegram/state_manager"
)
type Respondent struct {
Manager *stateManager.StateManager
}
func NewRespondent(m *stateManager.StateManager) *Respondent {
return &Respondent{
Manager: m,
}
}
func (r *Respondent) Respond(startState string) {
question := r.Manager.Questions[r.Manager.State]
if len(question.Buttons) > 0 {
var choice models.Button
for {
choice = question.Buttons[rand.Intn(len(question.Buttons))]
fmt.Println("choice it:", choice.Text)
if choice.State != startState {
break
}
}
r.Manager.Listener(choice.State)
}
}

27
respondent/respondent.v Normal file

@ -0,0 +1,27 @@
module respondent
import rand
import state_manager
pub struct Respondent {
pub mut:
manager &state_manager.StateManager
}
pub fn (mut r Respondent) respond(start_state string) {
mut question := r.manager.questions[r.manager.state]
if question.buttons.len > 0 {
mut choice := question.buttons[0]
for {
choice = question.buttons[rand.intn(question.buttons.len) or {
eprintln('Failed rand.intn: ${err}')
return
}]
//println('choice it: ${choice}')
if choice.state != start_state {
break
}
}
r.manager.listener(choice.state)
}
}

@ -0,0 +1,41 @@
package stateManager
import (
"fmt"
"penahub.gitlab.yandexcloud.net/backend/quiz/telegram/client"
"penahub.gitlab.yandexcloud.net/backend/quiz/telegram/models"
)
type StateManager struct {
Questions map[string]models.Question
State string
client *client.Client
}
type Deps struct {
Questions map[string]models.Question
State string
Client *client.Client
}
func NewStateManager(deps Deps) *StateManager {
return &StateManager{
Questions: deps.Questions,
State: deps.State,
client: deps.Client,
}
}
func (sm *StateManager) Listener(state string) {
sm.State = state
sm.SendQuestion()
}
func (sm *StateManager) SendQuestion() {
question := sm.Questions[sm.State]
message := fmt.Sprintf("%s\n%s", question.Title, question.Description)
for _, button := range question.Buttons {
message += fmt.Sprintf("\n%s", button.Text)
}
sm.client.SendMessage(message)
}

@ -0,0 +1,26 @@
module state_manager
import strconv
import models
import client
pub struct StateManager {
pub mut:
questions map[string]models.Question
state string
client &client.Client
}
pub fn (mut sm StateManager) listener(state string) {
sm.state = state
sm.send_question()
}
pub fn (mut sm StateManager) send_question() {
mut question := sm.questions[sm.state]
mut message := unsafe { strconv.v_sprintf('%s\n%s', question.title, question.description)}
for _, button in question.buttons {
message += unsafe { strconv.v_sprintf('\n%s', button.text)}
}
sm.client.send_message(message)
}

55
tg_handle/tg.v Normal file

@ -0,0 +1,55 @@
module tg_handle
import dariotarantini.vgram
pub struct TgBot {
pub:
bot vgram.Bot
}
pub fn new_tg_bot(token string){
bot := vgram.new_bot(token)
b := TgBot{
bot: bot
}
spawn b.start()
}
fn (b TgBot) start() ! {
mut new := true
mut last_offset := 0
for {
mut updates := b.bot.get_updates(offset: last_offset, limit: 100)
for update in updates {
if last_offset < update.update_id {
last_offset = update.update_id
if new{
new = false
break
}
if update.message.text == "/start" {
b.start_handler(update)!
}
else {
b.message_handler(update)!
}
}
}
}
}
fn (b TgBot) start_handler(update vgram.Update) ! {
b.bot.send_message(
chat_id: update.message.from.id.str(),
text: 'Привет!'
)
}
fn (b TgBot) message_handler(update vgram.Update) ! {
if !update.message.text.starts_with('/') {
b.bot.send_message(
chat_id: update.message.from.id.str(),
text: 'Кнопка: $update.message.text'
)
}
}

7184
treedata.json Normal file

File diff suppressed because it is too large Load Diff