Stage 11: account linking & merge (email + Telegram Login Widget)
Tests · Go / test (push) Successful in 7s
Tests · Integration / integration (push) Successful in 11s
Tests · UI / test (push) Successful in 20s
Tests · Go / test (pull_request) Successful in 6s
Tests · Integration / integration (pull_request) Successful in 11s
Tests · UI / test (pull_request) Successful in 19s

Link an email (confirm-code) or Telegram (web Login Widget) to the current
account; if the identity already has its own account, merge the two into the
one in use (the current account is primary, except a guest initiator whose
durable counterpart wins). The merge runs in one transaction
(internal/accountmerge): stats + hint wallet summed, paid_account ORed,
identities/games/chat/complaints transferred, friends/blocks de-duplicated,
the secondary kept as a merged_into tombstone so a shared finished game's
no-cascade FKs hold; a shared active game blocks the merge.

- migration 00009: accounts.paid_account, merged_into, merged_at (+ jetgen)
- internal/link orchestrator; session.RevokeAllForAccount on merge
- connector ValidateLoginWidget RPC + loginwidget HMAC validator
- edge ops link.email.request/confirm/merge, link.telegram.confirm/merge;
  supersedes the Stage 8 email.bind.* surface (request never reveals 'taken'
  before the code is verified, so a probe cannot enumerate addresses)
- UI Profile link section + irreversible-merge dialog; Telegram web sign-in
- focused regression tests (merge core, guest inversion, active-game refusal,
  finished-shared-game kept), gateway transcode + connector + UI codec/e2e
- docs: PLAN, ARCHITECTURE 3/4/9, FUNCTIONAL(+ru), module READMEs
This commit is contained in:
Ilia Denisov
2026-06-04 11:15:14 +02:00
parent 3a640a17a4
commit 52f898ca6f
68 changed files with 3331 additions and 369 deletions
+158 -36
View File
@@ -148,6 +148,115 @@ func (x *ValidateInitDataResponse) GetLanguageCode() string {
return ""
}
// ValidateLoginWidgetRequest carries the Login Widget result serialized as a URL
// query string (the widget fields plus the hash, e.g. "auth_date=...&id=...&hash=...").
type ValidateLoginWidgetRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Data string `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ValidateLoginWidgetRequest) Reset() {
*x = ValidateLoginWidgetRequest{}
mi := &file_telegram_v1_telegram_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ValidateLoginWidgetRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ValidateLoginWidgetRequest) ProtoMessage() {}
func (x *ValidateLoginWidgetRequest) ProtoReflect() protoreflect.Message {
mi := &file_telegram_v1_telegram_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ValidateLoginWidgetRequest.ProtoReflect.Descriptor instead.
func (*ValidateLoginWidgetRequest) Descriptor() ([]byte, []int) {
return file_telegram_v1_telegram_proto_rawDescGZIP(), []int{2}
}
func (x *ValidateLoginWidgetRequest) GetData() string {
if x != nil {
return x.Data
}
return ""
}
// ValidateLoginWidgetResponse is the validated identity. external_id is the
// Telegram user id used as the identities external_id. The Login Widget carries no
// language_code (unlike Mini App initData).
type ValidateLoginWidgetResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
ExternalId string `protobuf:"bytes,1,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"`
Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"`
FirstName string `protobuf:"bytes,3,opt,name=first_name,json=firstName,proto3" json:"first_name,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ValidateLoginWidgetResponse) Reset() {
*x = ValidateLoginWidgetResponse{}
mi := &file_telegram_v1_telegram_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ValidateLoginWidgetResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ValidateLoginWidgetResponse) ProtoMessage() {}
func (x *ValidateLoginWidgetResponse) ProtoReflect() protoreflect.Message {
mi := &file_telegram_v1_telegram_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ValidateLoginWidgetResponse.ProtoReflect.Descriptor instead.
func (*ValidateLoginWidgetResponse) Descriptor() ([]byte, []int) {
return file_telegram_v1_telegram_proto_rawDescGZIP(), []int{3}
}
func (x *ValidateLoginWidgetResponse) GetExternalId() string {
if x != nil {
return x.ExternalId
}
return ""
}
func (x *ValidateLoginWidgetResponse) GetUsername() string {
if x != nil {
return x.Username
}
return ""
}
func (x *ValidateLoginWidgetResponse) GetFirstName() string {
if x != nil {
return x.FirstName
}
return ""
}
// NotifyRequest addresses a push event to one recipient. kind is the backend push
// catalog kind (your_turn, nudge, match_found, notify); payload is the FlatBuffers
// scrabblefb.* body for that kind; language (en/ru) selects the message template.
@@ -163,7 +272,7 @@ type NotifyRequest struct {
func (x *NotifyRequest) Reset() {
*x = NotifyRequest{}
mi := &file_telegram_v1_telegram_proto_msgTypes[2]
mi := &file_telegram_v1_telegram_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -175,7 +284,7 @@ func (x *NotifyRequest) String() string {
func (*NotifyRequest) ProtoMessage() {}
func (x *NotifyRequest) ProtoReflect() protoreflect.Message {
mi := &file_telegram_v1_telegram_proto_msgTypes[2]
mi := &file_telegram_v1_telegram_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -188,7 +297,7 @@ func (x *NotifyRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use NotifyRequest.ProtoReflect.Descriptor instead.
func (*NotifyRequest) Descriptor() ([]byte, []int) {
return file_telegram_v1_telegram_proto_rawDescGZIP(), []int{2}
return file_telegram_v1_telegram_proto_rawDescGZIP(), []int{4}
}
func (x *NotifyRequest) GetExternalId() string {
@@ -230,7 +339,7 @@ type NotifyResponse struct {
func (x *NotifyResponse) Reset() {
*x = NotifyResponse{}
mi := &file_telegram_v1_telegram_proto_msgTypes[3]
mi := &file_telegram_v1_telegram_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -242,7 +351,7 @@ func (x *NotifyResponse) String() string {
func (*NotifyResponse) ProtoMessage() {}
func (x *NotifyResponse) ProtoReflect() protoreflect.Message {
mi := &file_telegram_v1_telegram_proto_msgTypes[3]
mi := &file_telegram_v1_telegram_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -255,7 +364,7 @@ func (x *NotifyResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use NotifyResponse.ProtoReflect.Descriptor instead.
func (*NotifyResponse) Descriptor() ([]byte, []int) {
return file_telegram_v1_telegram_proto_rawDescGZIP(), []int{3}
return file_telegram_v1_telegram_proto_rawDescGZIP(), []int{5}
}
func (x *NotifyResponse) GetDelivered() bool {
@@ -276,7 +385,7 @@ type SendToUserRequest struct {
func (x *SendToUserRequest) Reset() {
*x = SendToUserRequest{}
mi := &file_telegram_v1_telegram_proto_msgTypes[4]
mi := &file_telegram_v1_telegram_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -288,7 +397,7 @@ func (x *SendToUserRequest) String() string {
func (*SendToUserRequest) ProtoMessage() {}
func (x *SendToUserRequest) ProtoReflect() protoreflect.Message {
mi := &file_telegram_v1_telegram_proto_msgTypes[4]
mi := &file_telegram_v1_telegram_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -301,7 +410,7 @@ func (x *SendToUserRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use SendToUserRequest.ProtoReflect.Descriptor instead.
func (*SendToUserRequest) Descriptor() ([]byte, []int) {
return file_telegram_v1_telegram_proto_rawDescGZIP(), []int{4}
return file_telegram_v1_telegram_proto_rawDescGZIP(), []int{6}
}
func (x *SendToUserRequest) GetExternalId() string {
@@ -328,7 +437,7 @@ type SendToGameChannelRequest struct {
func (x *SendToGameChannelRequest) Reset() {
*x = SendToGameChannelRequest{}
mi := &file_telegram_v1_telegram_proto_msgTypes[5]
mi := &file_telegram_v1_telegram_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -340,7 +449,7 @@ func (x *SendToGameChannelRequest) String() string {
func (*SendToGameChannelRequest) ProtoMessage() {}
func (x *SendToGameChannelRequest) ProtoReflect() protoreflect.Message {
mi := &file_telegram_v1_telegram_proto_msgTypes[5]
mi := &file_telegram_v1_telegram_proto_msgTypes[7]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -353,7 +462,7 @@ func (x *SendToGameChannelRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use SendToGameChannelRequest.ProtoReflect.Descriptor instead.
func (*SendToGameChannelRequest) Descriptor() ([]byte, []int) {
return file_telegram_v1_telegram_proto_rawDescGZIP(), []int{5}
return file_telegram_v1_telegram_proto_rawDescGZIP(), []int{7}
}
func (x *SendToGameChannelRequest) GetText() string {
@@ -373,7 +482,7 @@ type SendResponse struct {
func (x *SendResponse) Reset() {
*x = SendResponse{}
mi := &file_telegram_v1_telegram_proto_msgTypes[6]
mi := &file_telegram_v1_telegram_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -385,7 +494,7 @@ func (x *SendResponse) String() string {
func (*SendResponse) ProtoMessage() {}
func (x *SendResponse) ProtoReflect() protoreflect.Message {
mi := &file_telegram_v1_telegram_proto_msgTypes[6]
mi := &file_telegram_v1_telegram_proto_msgTypes[8]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -398,7 +507,7 @@ func (x *SendResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use SendResponse.ProtoReflect.Descriptor instead.
func (*SendResponse) Descriptor() ([]byte, []int) {
return file_telegram_v1_telegram_proto_rawDescGZIP(), []int{6}
return file_telegram_v1_telegram_proto_rawDescGZIP(), []int{8}
}
func (x *SendResponse) GetDelivered() bool {
@@ -421,7 +530,15 @@ const file_telegram_v1_telegram_proto_rawDesc = "" +
"\busername\x18\x02 \x01(\tR\busername\x12\x1d\n" +
"\n" +
"first_name\x18\x03 \x01(\tR\tfirstName\x12#\n" +
"\rlanguage_code\x18\x04 \x01(\tR\flanguageCode\"z\n" +
"\rlanguage_code\x18\x04 \x01(\tR\flanguageCode\"0\n" +
"\x1aValidateLoginWidgetRequest\x12\x12\n" +
"\x04data\x18\x01 \x01(\tR\x04data\"y\n" +
"\x1bValidateLoginWidgetResponse\x12\x1f\n" +
"\vexternal_id\x18\x01 \x01(\tR\n" +
"externalId\x12\x1a\n" +
"\busername\x18\x02 \x01(\tR\busername\x12\x1d\n" +
"\n" +
"first_name\x18\x03 \x01(\tR\tfirstName\"z\n" +
"\rNotifyRequest\x12\x1f\n" +
"\vexternal_id\x18\x01 \x01(\tR\n" +
"externalId\x12\x12\n" +
@@ -437,9 +554,10 @@ const file_telegram_v1_telegram_proto_rawDesc = "" +
"\x18SendToGameChannelRequest\x12\x12\n" +
"\x04text\x18\x01 \x01(\tR\x04text\",\n" +
"\fSendResponse\x12\x1c\n" +
"\tdelivered\x18\x01 \x01(\bR\tdelivered2\x96\x03\n" +
"\tdelivered\x18\x01 \x01(\bR\tdelivered2\x92\x04\n" +
"\bTelegram\x12q\n" +
"\x10ValidateInitData\x12-.scrabble.telegram.v1.ValidateInitDataRequest\x1a..scrabble.telegram.v1.ValidateInitDataResponse\x12S\n" +
"\x10ValidateInitData\x12-.scrabble.telegram.v1.ValidateInitDataRequest\x1a..scrabble.telegram.v1.ValidateInitDataResponse\x12z\n" +
"\x13ValidateLoginWidget\x120.scrabble.telegram.v1.ValidateLoginWidgetRequest\x1a1.scrabble.telegram.v1.ValidateLoginWidgetResponse\x12S\n" +
"\x06Notify\x12#.scrabble.telegram.v1.NotifyRequest\x1a$.scrabble.telegram.v1.NotifyResponse\x12Y\n" +
"\n" +
"SendToUser\x12'.scrabble.telegram.v1.SendToUserRequest\x1a\".scrabble.telegram.v1.SendResponse\x12g\n" +
@@ -457,27 +575,31 @@ func file_telegram_v1_telegram_proto_rawDescGZIP() []byte {
return file_telegram_v1_telegram_proto_rawDescData
}
var file_telegram_v1_telegram_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_telegram_v1_telegram_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
var file_telegram_v1_telegram_proto_goTypes = []any{
(*ValidateInitDataRequest)(nil), // 0: scrabble.telegram.v1.ValidateInitDataRequest
(*ValidateInitDataResponse)(nil), // 1: scrabble.telegram.v1.ValidateInitDataResponse
(*NotifyRequest)(nil), // 2: scrabble.telegram.v1.NotifyRequest
(*NotifyResponse)(nil), // 3: scrabble.telegram.v1.NotifyResponse
(*SendToUserRequest)(nil), // 4: scrabble.telegram.v1.SendToUserRequest
(*SendToGameChannelRequest)(nil), // 5: scrabble.telegram.v1.SendToGameChannelRequest
(*SendResponse)(nil), // 6: scrabble.telegram.v1.SendResponse
(*ValidateInitDataRequest)(nil), // 0: scrabble.telegram.v1.ValidateInitDataRequest
(*ValidateInitDataResponse)(nil), // 1: scrabble.telegram.v1.ValidateInitDataResponse
(*ValidateLoginWidgetRequest)(nil), // 2: scrabble.telegram.v1.ValidateLoginWidgetRequest
(*ValidateLoginWidgetResponse)(nil), // 3: scrabble.telegram.v1.ValidateLoginWidgetResponse
(*NotifyRequest)(nil), // 4: scrabble.telegram.v1.NotifyRequest
(*NotifyResponse)(nil), // 5: scrabble.telegram.v1.NotifyResponse
(*SendToUserRequest)(nil), // 6: scrabble.telegram.v1.SendToUserRequest
(*SendToGameChannelRequest)(nil), // 7: scrabble.telegram.v1.SendToGameChannelRequest
(*SendResponse)(nil), // 8: scrabble.telegram.v1.SendResponse
}
var file_telegram_v1_telegram_proto_depIdxs = []int32{
0, // 0: scrabble.telegram.v1.Telegram.ValidateInitData:input_type -> scrabble.telegram.v1.ValidateInitDataRequest
2, // 1: scrabble.telegram.v1.Telegram.Notify:input_type -> scrabble.telegram.v1.NotifyRequest
4, // 2: scrabble.telegram.v1.Telegram.SendToUser:input_type -> scrabble.telegram.v1.SendToUserRequest
5, // 3: scrabble.telegram.v1.Telegram.SendToGameChannel:input_type -> scrabble.telegram.v1.SendToGameChannelRequest
1, // 4: scrabble.telegram.v1.Telegram.ValidateInitData:output_type -> scrabble.telegram.v1.ValidateInitDataResponse
3, // 5: scrabble.telegram.v1.Telegram.Notify:output_type -> scrabble.telegram.v1.NotifyResponse
6, // 6: scrabble.telegram.v1.Telegram.SendToUser:output_type -> scrabble.telegram.v1.SendResponse
6, // 7: scrabble.telegram.v1.Telegram.SendToGameChannel:output_type -> scrabble.telegram.v1.SendResponse
4, // [4:8] is the sub-list for method output_type
0, // [0:4] is the sub-list for method input_type
2, // 1: scrabble.telegram.v1.Telegram.ValidateLoginWidget:input_type -> scrabble.telegram.v1.ValidateLoginWidgetRequest
4, // 2: scrabble.telegram.v1.Telegram.Notify:input_type -> scrabble.telegram.v1.NotifyRequest
6, // 3: scrabble.telegram.v1.Telegram.SendToUser:input_type -> scrabble.telegram.v1.SendToUserRequest
7, // 4: scrabble.telegram.v1.Telegram.SendToGameChannel:input_type -> scrabble.telegram.v1.SendToGameChannelRequest
1, // 5: scrabble.telegram.v1.Telegram.ValidateInitData:output_type -> scrabble.telegram.v1.ValidateInitDataResponse
3, // 6: scrabble.telegram.v1.Telegram.ValidateLoginWidget:output_type -> scrabble.telegram.v1.ValidateLoginWidgetResponse
5, // 7: scrabble.telegram.v1.Telegram.Notify:output_type -> scrabble.telegram.v1.NotifyResponse
8, // 8: scrabble.telegram.v1.Telegram.SendToUser:output_type -> scrabble.telegram.v1.SendResponse
8, // 9: scrabble.telegram.v1.Telegram.SendToGameChannel:output_type -> scrabble.telegram.v1.SendResponse
5, // [5:10] is the sub-list for method output_type
0, // [0:5] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
@@ -494,7 +616,7 @@ func file_telegram_v1_telegram_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_telegram_v1_telegram_proto_rawDesc), len(file_telegram_v1_telegram_proto_rawDesc)),
NumEnums: 0,
NumMessages: 7,
NumMessages: 9,
NumExtensions: 0,
NumServices: 1,
},
+20
View File
@@ -20,6 +20,11 @@ service Telegram {
// the authenticated user. The gateway calls it during the auth.telegram edge
// operation, then provisions the session through the backend internal API.
rpc ValidateInitData(ValidateInitDataRequest) returns (ValidateInitDataResponse);
// ValidateLoginWidget verifies Telegram Login Widget authorization data (the web
// sign-in flow, HMAC under SHA-256(bot_token)) and returns the authenticated
// user. The gateway calls it during the link.telegram edge operation to attach a
// Telegram identity to an existing account (Stage 11).
rpc ValidateLoginWidget(ValidateLoginWidgetRequest) returns (ValidateLoginWidgetResponse);
// Notify delivers an out-of-app notification for a backend push event. The
// gateway calls it only for a recipient with no live in-app stream (so the
// platform push never duplicates in-app delivery). The connector renders a
@@ -50,6 +55,21 @@ message ValidateInitDataResponse {
string language_code = 4;
}
// ValidateLoginWidgetRequest carries the Login Widget result serialized as a URL
// query string (the widget fields plus the hash, e.g. "auth_date=...&id=...&hash=...").
message ValidateLoginWidgetRequest {
string data = 1;
}
// ValidateLoginWidgetResponse is the validated identity. external_id is the
// Telegram user id used as the identities external_id. The Login Widget carries no
// language_code (unlike Mini App initData).
message ValidateLoginWidgetResponse {
string external_id = 1;
string username = 2;
string first_name = 3;
}
// NotifyRequest addresses a push event to one recipient. kind is the backend push
// catalog kind (your_turn, nudge, match_found, notify); payload is the FlatBuffers
// scrabblefb.* body for that kind; language (en/ru) selects the message template.
+50 -4
View File
@@ -30,10 +30,11 @@ import (
const _ = grpc.SupportPackageIsVersion9
const (
Telegram_ValidateInitData_FullMethodName = "/scrabble.telegram.v1.Telegram/ValidateInitData"
Telegram_Notify_FullMethodName = "/scrabble.telegram.v1.Telegram/Notify"
Telegram_SendToUser_FullMethodName = "/scrabble.telegram.v1.Telegram/SendToUser"
Telegram_SendToGameChannel_FullMethodName = "/scrabble.telegram.v1.Telegram/SendToGameChannel"
Telegram_ValidateInitData_FullMethodName = "/scrabble.telegram.v1.Telegram/ValidateInitData"
Telegram_ValidateLoginWidget_FullMethodName = "/scrabble.telegram.v1.Telegram/ValidateLoginWidget"
Telegram_Notify_FullMethodName = "/scrabble.telegram.v1.Telegram/Notify"
Telegram_SendToUser_FullMethodName = "/scrabble.telegram.v1.Telegram/SendToUser"
Telegram_SendToGameChannel_FullMethodName = "/scrabble.telegram.v1.Telegram/SendToGameChannel"
)
// TelegramClient is the client API for Telegram service.
@@ -46,6 +47,11 @@ type TelegramClient interface {
// the authenticated user. The gateway calls it during the auth.telegram edge
// operation, then provisions the session through the backend internal API.
ValidateInitData(ctx context.Context, in *ValidateInitDataRequest, opts ...grpc.CallOption) (*ValidateInitDataResponse, error)
// ValidateLoginWidget verifies Telegram Login Widget authorization data (the web
// sign-in flow, HMAC under SHA-256(bot_token)) and returns the authenticated
// user. The gateway calls it during the link.telegram edge operation to attach a
// Telegram identity to an existing account (Stage 11).
ValidateLoginWidget(ctx context.Context, in *ValidateLoginWidgetRequest, opts ...grpc.CallOption) (*ValidateLoginWidgetResponse, error)
// Notify delivers an out-of-app notification for a backend push event. The
// gateway calls it only for a recipient with no live in-app stream (so the
// platform push never duplicates in-app delivery). The connector renders a
@@ -79,6 +85,16 @@ func (c *telegramClient) ValidateInitData(ctx context.Context, in *ValidateInitD
return out, nil
}
func (c *telegramClient) ValidateLoginWidget(ctx context.Context, in *ValidateLoginWidgetRequest, opts ...grpc.CallOption) (*ValidateLoginWidgetResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ValidateLoginWidgetResponse)
err := c.cc.Invoke(ctx, Telegram_ValidateLoginWidget_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *telegramClient) Notify(ctx context.Context, in *NotifyRequest, opts ...grpc.CallOption) (*NotifyResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(NotifyResponse)
@@ -119,6 +135,11 @@ type TelegramServer interface {
// the authenticated user. The gateway calls it during the auth.telegram edge
// operation, then provisions the session through the backend internal API.
ValidateInitData(context.Context, *ValidateInitDataRequest) (*ValidateInitDataResponse, error)
// ValidateLoginWidget verifies Telegram Login Widget authorization data (the web
// sign-in flow, HMAC under SHA-256(bot_token)) and returns the authenticated
// user. The gateway calls it during the link.telegram edge operation to attach a
// Telegram identity to an existing account (Stage 11).
ValidateLoginWidget(context.Context, *ValidateLoginWidgetRequest) (*ValidateLoginWidgetResponse, error)
// Notify delivers an out-of-app notification for a backend push event. The
// gateway calls it only for a recipient with no live in-app stream (so the
// platform push never duplicates in-app delivery). The connector renders a
@@ -145,6 +166,9 @@ type UnimplementedTelegramServer struct{}
func (UnimplementedTelegramServer) ValidateInitData(context.Context, *ValidateInitDataRequest) (*ValidateInitDataResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ValidateInitData not implemented")
}
func (UnimplementedTelegramServer) ValidateLoginWidget(context.Context, *ValidateLoginWidgetRequest) (*ValidateLoginWidgetResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ValidateLoginWidget not implemented")
}
func (UnimplementedTelegramServer) Notify(context.Context, *NotifyRequest) (*NotifyResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Notify not implemented")
}
@@ -193,6 +217,24 @@ func _Telegram_ValidateInitData_Handler(srv interface{}, ctx context.Context, de
return interceptor(ctx, in, info, handler)
}
func _Telegram_ValidateLoginWidget_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ValidateLoginWidgetRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(TelegramServer).ValidateLoginWidget(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Telegram_ValidateLoginWidget_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(TelegramServer).ValidateLoginWidget(ctx, req.(*ValidateLoginWidgetRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Telegram_Notify_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(NotifyRequest)
if err := dec(in); err != nil {
@@ -258,6 +300,10 @@ var Telegram_ServiceDesc = grpc.ServiceDesc{
MethodName: "ValidateInitData",
Handler: _Telegram_ValidateInitData_Handler,
},
{
MethodName: "ValidateLoginWidget",
Handler: _Telegram_ValidateLoginWidget_Handler,
},
{
MethodName: "Notify",
Handler: _Telegram_Notify_Handler,