The task is that user input a sum of deposit and I could handle it in this context, but isn't like a simple command. Example:
My code:
func main () {
testing()
NewBot, BotError = tgBotApi.NewBotAPI(configuration.BOT_TOKEN)
if BotError != nil {
fmt.Println(BotError.Error())
}
NewBot.Debug = true
fmt.Println("OK", time.Now().Unix(), time.Now(), time.Now().Weekday())
setWebhook(NewBot)
updates := NewBot.ListenForWebhook("/" + configuration.BOT_TOKEN)
//go successfulPaymentListen()
go http.ListenAndServeTLS(fmt.Sprintf("%s:%s", configuration.BOT_HOST, configuration.BOT_PORT), configuration.CERT_FILE, configuration.CERT_KEY, nil)
for update := range updates {
if update.Message != nil {
recognizeCommand(update)
} else if update.CallbackQuery != nil {
if update.CallbackQuery.Data == "/addFunds crypto" {
get_data.AddFundsChooseCurrencyCrypto(update, NewBot)
} else if update.CallbackQuery.Data == "/addFunds qiwi" {
get_data.AddFundsChooseCurrencyQiwi(update, NewBot)
} else if strings.Split(update.CallbackQuery.Data, " ")[2] != "" {
get_data.AddFundsChooseCurrencyCurrentCrypto(update, NewBot, strings.Split(update.CallbackQuery.Data, " ")[2])
//This function is below
}
}
}
}
get_data.AddFundsChooseCurrencyCurrentCrypto:
func AddFundsChooseCurrencyCurrentCrypto(update tgBotApi.Update, NewBot *tgBotApi.BotAPI, currency string) {
chatUser := int64(update.CallbackQuery.From.ID)
msg := tgBotApi.NewMessage(chatUser, "Input a sum of deposit:")
NewBot.Send(msg)
//There is I have to handle user answer, but I can't override ListenWebHook
}
The problem is that I need ListenWebHook localy( in the function AddFundsChooseCurrencyCurrentCrypto) instead of main function
------------------------ UPDATE ------------------------
I have tried this code:
func AddFundsChooseCurrencyCurrentCrypto(update tgBotApi.Update, NewBot *tgBotApi.BotAPI, currency string) {
chatUser := int64(update.CallbackQuery.From.ID)
msg := tgBotApi.NewMessage(chatUser, "Input a sum of deposit:")
NewBot.Send(msg)
NewBotContext, BotError := tgBotApi.NewBotAPI(configuration.BOT_TOKEN)
if BotError != nil {
log.Panic(BotError.Error())
}
updates := NewBotContext.ListenForWebhook("/" + configuration.BOT_TOKEN)
for update := range updates {
fmt.Println(update)
}
}
But error:
panic: http: multiple registrations for /mytokenbot
goroutine 1 [running]:
net/http.(*ServeMux).Handle(0xe38620, 0xc25304, 0x2f, 0xc7dbe0, 0xc00018bec0)
You've tried to register the same url '/mytokenbot' twice with your router. You can find the error in net/http:
https://golang.org/src/net/http/server.go#L2433
In the mux Handle function.
So just look through your code for the register function with servemux, and check how you might be calling it twice.
Related
I have a map as follows:
groupMemberStore map[groupMemberId]*groupMember
and it's initialized like that:
ih.groupMemberStore = make(map[groupMemberId]*groupMember)
And I have a RWMutex to synchronize read-write operations. So the problem is, I populate the map with an element and 1 or 2 out of 10 times it is coming as empty even though I know that there's a record.
func (ih *IgmpHandler) GetGroup(ctx context.Context, groupId uint32) *igmpca.Group {
group := &igmpca.Group{}
ih.mapMutex.RLock()
defer ih.mapMutex.RUnlock()
log.Println("gms: ", ih.groupMemberStore)
for k, v := range ih.groupMemberStore {
log.Println("k.ipAddress:", k.ipAddress)
id, err := ih.deviceConfigurator.getGroupId(ctx, k.ipAddress)
fmt.Println("err: ", err)
fmt.Println("groupId in GetGroup", groupId, id)
if err == nil && groupId == id {
member := &igmpca.Member{
GemPortId: v.gemPortId,
IntfId: k.intfId,
SrcIpAddress: v.ipv4Src.String(),
Vlan: uint32(v.vlan),
IsV2: v.isV2,
OnuId: k.onuId,
UniId: k.uniId,
}
// group := &igmpca.Group{}
if group.Members == nil {
gid := &igmpca.GroupId{Id: groupId}
members := &igmpca.Members{}
members.Members = append(members.Members, member)
group.Id = gid
group.GroupIp = k.ipAddress
group.Members = members
} else {
group.Members.Members = append(group.Members.Members, member)
}
}
}
if group.Members != nil {
group.Members.NumberOfMembers = uint32(len(group.Members.Members))
} else {
log.Println("EVET NIL", group.Members)
}
log.Println("GetGroup -> ", group)
return group
}
The line I print log.Println(ih.groupMemberStore) returns map[] somethimes and sometimes returns the relevant information. However, during all the invocations I'm sure that there's no delete operation on this map or anything like that. Is there someting I'm missing? Is something related to Mutexes or pointers?
Below functions represent the operations on groupMemberStore as get, write and delete respectively.
func (ih *IgmpHandler) getFromGroupMemberStore(key groupMemberId) (*groupMember, bool) {
ih.mapMutex.RLock()
g, exists := ih.groupMemberStore[key]
ih.mapMutex.RUnlock()
return g, exists
}
func (ih *IgmpHandler) writeToGroupMemberStore(key groupMemberId, value groupMember) {
log.Println("writeToGroupMemberStore")
ih.mapMutex.Lock()
ih.groupMemberStore[key] = &value
ih.mapMutex.Unlock()
}
func (ih *IgmpHandler) deleteFromGroupMemberStore(key groupMemberId) {
ih.mapMutex.Lock()
delete(ih.groupMemberStore, key)
ih.mapMutex.Unlock()
}
This is the getGroupId
func (idc *IgmpDeviceConfigurator) getGroupId(ctx context.Context, groupIp string) (uint32, error) {
idc.lock.RLock()
defer idc.lock.RUnlock()
gId, exists := idc.groupIdMap[groupIp]
if !exists {
return 0, fmt.Errorf("group-not-found-for-%s", groupIp)
}
return gId, nil
}
This should be cause of race condition. Please follow as below.
// NewIgmpHandler creates an instance of IgmpHandler.
func NewIgmpHandler() IgmpHandler {
return IgmpHandler{
groupMemberStore: make(map[groupMemberId]*groupMember),
}
}
// OR
// NewIgmpHandler creates an instance of IgmpHandler.
func NewIgmpHandler(groupMemberStore map[groupMemberId]*groupMember) IgmpHandler {
return IgmpHandler{
groupMemberStore: groupMemberStore,
}
}
// Add adds a group member to the map.
func (ih *IgmpHandler) Add(gid groupMemberId, gMem *groupMember) {
ih.mapMutex.Lock()
defer ih.mapMutex.Unlock()
// Add new element to the map here. When you are updating the map, you
// should take the write lock and It prevents others to read the map.
}
// GetGroup returns igmpca.Group by group Id.
func (ih *IgmpHandler) GetGroup(ctx context.Context, groupId uint32) *igmpca.Group {
ih.mapMutex.RLock()
defer ih.mapMutex.RUnlock()
group := &igmpca.Group{}
for gid, gMem := range ih.groupMemberStore {
...
}
...
}
So I was trying to create a mock DB, and in the current implementation, I am trying to make an insert and select which insert rows and select returns them. I decided to use a bytes.Buffer to help maintain a memory block I could insert a slice of rows in, and deserialize that memory block when I call select but it seems select just returns the first row instead of all the rows passed to the array.
main.go
func main() {
inputBuffer := compiler.NewInputBuffer()
scanner := bufio.NewScanner(os.Stdin)
for {
PrintPrompt()
scanner.Scan()
command := scanner.Text()
inputBuffer.Buffer = command
if strings.HasPrefix(inputBuffer.Buffer, ".") {
switch compiler.DoMetaCommand(inputBuffer) {
case compiler.MetaCommandSuccess:
continue
case compiler.MetaCommandUnrecognizedCommand:
fmt.Printf("Unrecognized command %q \n", inputBuffer.Buffer)
continue
}
}
var statement compiler.Statement
switch compiler.PrepareStatement(inputBuffer, &statement) {
case compiler.PrepareSuccess:
case compiler.PrepareUnrecognizedStatement:
fmt.Printf("Unrecognized command at start of %q \n", inputBuffer.Buffer)
continue
case compiler.PrepareSyntaxError:
fmt.Println("Syntax error. Could not parse statement.")
continue
}
compiler.ExecuteStatement(statement)
fmt.Println("Executed")
}
}
func PrintPrompt() {
fmt.Printf("db > ")
}
Above is the code responsible for collecting user input etc.
package compiler
import (
"bytes"
"log"
"os"
"strconv"
"strings"
)
type Row struct {
ID int32
Username string
Email string
}
type Statement struct {
RowToInsert Row
Type StatementType
}
var (
RowsTable = make([]Row, 0)
RowsTableBuffer bytes.Buffer
)
func DoMetaCommand(buffer InputBuffer) MetaCommandResult {
if buffer.Buffer == ".exit" {
os.Exit(0)
} else {
return MetaCommandUnrecognizedCommand
}
return MetaCommandSuccess
}
func PrepareStatement(buffer InputBuffer, statement *Statement) PrepareResult {
if len(buffer.Buffer) > 6 {
bufferArguments := strings.Fields(buffer.Buffer)
if bufferArguments[0] == "insert" {
statement.Type = StatementInsert
if len(bufferArguments) < 4 {
return PrepareSyntaxError
} else {
i, err := strconv.Atoi(bufferArguments[1])
if err != nil {
log.Printf("%q is not a valid id\n", bufferArguments[1])
return PrepareSyntaxError
} else {
statement.RowToInsert.ID = int32(i)
}
statement.RowToInsert.Username = bufferArguments[2]
statement.RowToInsert.Email = bufferArguments[3]
}
RowsTable = append(RowsTable, statement.RowToInsert)
return PrepareSuccess
}
}
if buffer.Buffer == "select" {
statement.Type = StatementSelect
return PrepareSuccess
}
return PrepareUnrecognizedStatement
}
func ExecuteStatement(statement Statement) {
switch statement.Type {
case (StatementInsert):
SerializeRow(RowsTable)
case (StatementSelect):
DeserializeRow()
}
}
The code above is for parsing and appending the entries into statements and depending on the keywords, it's either an insert or select [Took the code for defining enums out and left core logic]
func SerializeRow(r []Row) {
encoder := gob.NewEncoder(&RowsTableBuffer)
err := encoder.Encode(r)
if err != nil {
log.Println("encode error:", err)
}
}
func DeserializeRow() {
var rowsBuffer = RowsTableBuffer
rowsTable := make([]Row, 0)
decoder := gob.NewDecoder(&rowsBuffer)
err := decoder.Decode(&rowsTable)
if err != nil {
log.Println("decode error:", err)
}
fmt.Println(rowsTable)
}
So the code above uses a global buffer in which the slice being appended to in PrepareStatement()will be encoded after an insert is done. A select ought to return the slice of all rows but just returns the first element for some reason.
Example (in terminal):
If I make 2 inserts:
db > insert 1 john c#mail.com
db > insert 2 collins k#mail.com
Then I make a select:
select
=> it returns [{1 john c#mail.com}] only.
Is there anything I am missing here? Thanks for your support.
So the answer was pretty simple. We were creating a new encoder in the SerializeRow function instead of creating it once. We pulled it out of the function and created a global.
var (
encoder = gob.NewEncoder(&RowsTableBuffer)
decoder = gob.NewDecoder(&RowsTableBuffer)
)
func SerializeRow(r Row) {
err := encoder.Encode(r)
if err != nil {
log.Println("encode error:", err)
}
}
func DeserializeRow() {
var rows Row
err := decoder.Decode(&rows)
for err == nil {
if err != nil {
log.Fatal("decode error:", err)
}
fmt.Printf("%d %s %s\n", rows.ID, rows.Username, rows.Email)
err = decoder.Decode(&rows)
}
}
I am trying to work with the Ethereum blockchain, with the Solidity contracts.
I am currently deploying a contract and performing some actions on it, but I would like to know how to get some 'feedback/callback/returns' of a specific transaction.
Is there a way to set the status of a transaction to 0 (error) and still get events, for example ?
if (id.length <= 0) {
emit Result("KO", "1");
revert();
}
This will not work (no event) because I revert everything, but the status will be set to 0
if (id.length <= 0) {
emit Result("KO", "1");
return;
}
I will get some event, but the status will stay 1
if (id.length <= 0) {
revert("KO_1");
}
The status will be 0, but I will not have any event
Here is my go code to perform the action :
func testFunction(id []byte) {
//...
//...
tx, err := instance.Action(opt, id)
if (errors.HasError(err)) {
return
}
callbackValue := subscribeToContract(tx.Hash().Hex())
logs.Pretty(tx, callbackValue)
//...
//...
}
func subscribeToContract(hashToRead string) myStruct {
query := ethereum.FilterQuery{
Addresses: []common.Address{address},
}
soc := make(chan types.Log)
sub, err := WssClient.SubscribeFilterLogs(context.Background(), query, soc)
if err != nil {
logs.Error(err)
}
for {
select {
case err := <-sub.Err():
logs.Info(`SOMETHING ERROR`)
logs.Error(err)
case vLog := <-soc:
logs.Info(`SOMETHING`)
contractAbi, _ := abi.JSON(strings.NewReader(string(SignABI)))
event := myStruct{}
contractAbi.Unpack(&event, "Result", vLog.Data)
logs.Info(`New Event from [` + vLog.TxHash.Hex() + `] : ` + event.Message)
}
}
}
If id.length > 0, all good.
But if id.length <= 0, I have no callback from the subscribeToContract function.
Is there a way to directly have the result status, or should loop with tx, err := client.TransactionReceipt(context.Background(), txHash) until I get a Status?
I didn't found any way to subscribe to a specific status change for a transaction, but a workaround :
The go-ethereum package provides 2 functions SubscribeFilterLogs and SubscribeNewHead. We can use the first one to get the logs (if relevant) and the second one to get a block information :
SubscribeNewHead subscribes to notifications about the current
blockchain head on the given channel.
A transaction can be validated or rejected/reverted when a block is mined, so we can use this 'trick'
func checkTransactionReceipt(_txHash string) int {
client, _ := getClient("https://ropsten.infura.io/v3/XXXXXX")
txHash := common.HexToHash(_txHash)
tx, err := client.TransactionReceipt(context.Background(), txHash)
if (Error.HasError(err)) {
return (-1)
}
return (int(tx.Status))
}
func WaitForBlockCompletation(data EthData, hashToRead string) int {
soc := make(chan *types.Header)
sub, err := data.WssClient.SubscribeNewHead(context.Background(), soc)
if (err != nil) {
return -1
}
for {
select {
case err := <-sub.Err():
_ = err
return -1
case header := <-soc:
logs.Info(header.TxHash.Hex())
transactionStatus := checkTransactionReceipt(hashToRead)
if (transactionStatus == 0) {
//FAILURE
sub.Unsubscribe()
return 0
} else if (transactionStatus == 1) {
//SUCCESS
sub.Unsubscribe()
return 1
}
}
}
}
Basically we are waiting for the block to be mined, then we check the TransactionReceipt which fail with an error (not found) if the transaction is not yet validated/rejected. Then, if the transaction is, we can unsubscribe the subscription and return the transaction status (0 fail, 1 success).
Not sure if it's the worst, best, only method, but it's working ! Be free to improve this solution !
An easier/newer solution:
I think the function waitMined is the function you are looking for.
bind.WaitMined(context.Background(), client, signedTx)
Originally posted in here.
I want to create a function that takes string argument, and depending of string returns different types
In my current project I am using Go version 1.11.4 for Rest API, PostgreSQL 11 for storing data.
The function is for avoiding the code redundancy, I just need one function for handling the requests, whithout this function I would be write more many request handlers.
Functions is:
func GetArrayOfModelStructs(table_name string) interface{} {
if table_name == "prefs_n_cities" {
Prefs_n_cities_array := &models.Prefs_n_cities_array{}
return Prefs_n_cities_array
} else if table_name == "position_owner" {
Position_owners := &models.Position_owners{}
return Position_owners
} else if table_name == "region" {
Regions := &models.Regions{}
return Regions
} else if table_name == "district" {
Districts := &models.Districts{}
return Districts
} else if table_name == "locality" {
Localities := &models.Localities{}
return Localities
} else if table_name == "locality_type" {
Locality_types := &models.Locality_types{}
return Locality_types
} else if table_name == "population" {
Populations := &models.Populations{}
return Populations
}
return nil
}
func GetModelStructs(table_name string) interface{} {
if table_name == "prefs_n_cities" {
return models.Prefs_n_cities{}
} else if table_name == "position_owner" {
return models.Position_owner{}
} else if table_name == "region" {
return models.Regions{}
} else if table_name == "district" {
return models.District{}
} else if table_name == "locality" {
return models.Locality{}
} else if table_name == "locality_type" {
return models.Locality_type{}
} else if table_name == "population" {
return models.Population{}
}
return nil
}
My structs example like:
type Prefs_n_cities struct {
id int32
Name string
Description string
Region_id int32
}
type Prefs_n_cities_array struct {
Array []Prefs_n_cities
}
And all of this I trying to use in another package for handling requests:
var GetData = func(res http.ResponseWriter, req *http.Request) {
// u.Logging(req, false)
db_name := req.URL.Query().Get("table_name")
data := u.GetArrayOfModelStructs(db_name)
spew.Dump(data)
sqlString := `SELECT * FROM "new_rollout_managment".` + db_name
rows, err := database.DB.Query(sqlString)
if err != nil {
http.Error(res, err.Error(), 400)
return
}
defer rows.Close()
for rows.Next() {
model := u.GetModelStructs(db_name)
err = rows.Scan(&model)
if err != nil {
http.Error(res, err.Error(), 400)
return
}
data.Array = append(data.Array, model)
}
err = rows.Err()
if err != nil {
http.Error(res, err.Error(), 400)
return
}
out, err := json.Marshal(models)
if err != nil {
http.Error(res, err.Error(), 500)
return
}
resp := u.Message(true, "Array of dictionaries")
resp["Dictionaries"] = out
u.Respond(res, resp)
}
My router is:
func Use(router *mux.Router) {
router.HandleFunc("/api/test/new", controllers.TestFunc).Methods("POST")
router.HandleFunc("/api/dictionaries/getdata", controllers.GetData).Methods("GET")
}
But in the GetData handler I have an error:
data.Array undefined (type interface {} is interface with no methods)
You can't do that. But you have two options:
Return an interface{}, as you are doing, then convert to a concrete type. This probably defeats your purpose.
Pass in your destination as a pointer, and modify it in place. So something like:
func GetArrayOfModelStructs(table_name string, dst interface{}) error {
Then have your function put the value in dst. This does require that the value passed as dst is a pointer. This is a very common pattern, seen in json.Unmarshal(), in sql's Rows.Scan, and many other places in the standard library and elsewhere.
I was trying to implement sort-of a reactive golang implementation. I have an array of observers. They are just a bunch of channels. Everything is encapsulated in a package where other code could subscribe and unsubscribe. When ever an order is created, the change will be pushed. But I have failed to register channel receive with in a method.
package rxOrder
import (
"fmt"
"time"
"errors"
"gopkg.in/mgo.v2/bson"
)
// Order This is the sample data structure
type Order struct {
id bson.ObjectId
moldID bson.ObjectId
bomID bson.ObjectId
deviceID bson.ObjectId
userIds []bson.ObjectId
name string
orderType string // withOrder, noOrder, makeUp, test
startTime time.Time
deadline time.Time
volume int32
}
// OrderMutation This is the struct for sending
// mutations to observers
type OrderMutation struct {
order Order
action string
}
// RxOrder This is the node for reactive Order
// management
type RxOrder struct {
orders []Order
observers map[string]chan OrderMutation
}
// init This method initialize RxOrder, including
// orders slice and subscriber map, user cannot
// initialize a RxOrder object more than once
func (rx *RxOrder) init() error {
if len(rx.orders) == 0 && len(rx.observers) == 0 {
rx.orders = make([]Order, 1)
rx.observers = make(map[string]chan OrderMutation)
return nil
}
return errors.New("Cannot reinitialize orders")
}
// subscribe, add observer to list
func (rx *RxOrder) subscribe(key string, ch chan OrderMutation) error {
if _, ok := rx.observers[key]; ok {
return errors.New("Observer already existed")
}
rx.observers[key] = ch
return nil
}
// unsubscribe, delete observer from list
func (rx *RxOrder) unsubscribe(key string) error {
if _, ok := rx.observers[key]; !ok {
return errors.New("Observer does not exist")
}
delete(rx.observers, key)
return nil
}
// createOrder The method for creating an order
func (rx *RxOrder) createOrder(order Order) error {
if !order.id.Valid() {
return errors.New("Invalid order id")
}
if !order.bomID.Valid() {
return errors.New("Invalid bom id")
}
if !order.deviceID.Valid() {
return errors.New("Invalid device id")
}
if !order.moldID.Valid() {
return errors.New("Invalid mold id")
}
if len(order.userIds) < 1 {
return errors.New("Empty users list")
}
for index, userID := range order.userIds {
if !userID.Valid() {
return errors.New(fmt.Sprint("Invalid user id at index: ", index))
}
}
if len(order.name) < 1 {
return errors.New("Empty order name")
}
if order.orderType != "withOrder" && order.orderType != "noOrder" && order.orderType != "makeUp" && order.orderType != "test" {
return errors.New("Wrong order type")
}
if order.startTime.After(order.deadline) {
return errors.New("Deadline cannot come before start time")
}
if order.volume < 1 {
return errors.New("Empty order is not accepted")
}
rx.orders = append(rx.orders, order)
for _, ch := range rx.observers {
ch <- OrderMutation{order, "create"}
}
return nil
}
func TestCreateOrder(t *testing.T) {
orderManagement := RxOrder{}
orderManagement.init()
orderManagement.subscribe("123", make(chan OrderMutation))
orderManagement.subscribe("345", make(chan OrderMutation))
orderManagement.subscribe("768", make(chan OrderMutation))
order := Order{}
order.id = bson.NewObjectId()
order.bomID = bson.NewObjectId()
order.deviceID = bson.NewObjectId()
order.moldID = bson.NewObjectId()
order.name = "iPhone 8+"
order.orderType = "withOrder"
order.volume = 5
order.startTime = time.Now()
order.deadline = order.startTime.AddDate(0, 1, 1)
order.userIds = make([]bson.ObjectId, 1)
order.userIds = append(order.userIds, bson.NewObjectId())
go func(t *testing.T) {
fmt.Println(<-orderManagement.observers["123"])
}(t)
orderManagement.createOrder(order)
//orderManagement.observers["123"] <- OrderMutation{order, "w"}
t.Fail()
}
when I do test, the above code prints nothing, but if I uncomment line:
orderManagement.observers["123"] <- OrderMutation{order, "w"}
Everything works. It seems I cannot operate on channel within a method. How can I encapsulate channel operation with in package?
The situation depends on the following outputs:
Does you createOrder return an error?
If createOrder has any error,then it will not send any message on the channel, so by commenting that line your main test function will exit without waiting.
You have no output, if you add the mentioned line, your main test function will wait on the channel operation until your coroutine receives the message and prints the output.
If your createOrder has no error, you will face a race condition because the message sent on channel except "123" will block your main test function forever.
After I did this, everything works.
go func() {
for _, ch: = range rx.observers {
ch <-OrderMutation {
order, "create"
}
}
}()
// create a goroutine to send message
func() {
orderManagement.createOrder(order)
}()
select {
case val := <-orderManagement.observers["123"]:
fmt.Println(val)
}
// then receive on the outside like this
Even without resolve method everything works.