Implement the avatar and a preflight dry run
The avatar is now applied rather than deferred: BotFather's /setuserpic is a conversation in which you send a photo, so the file is uploaded and sent as a message. It is content addressed -- replacing the file is what triggers an update, and the digest is recorded only after BotFather confirms, so a failed upload retries rather than being remembered as done. The image is validated before the conversation starts, because an image rejected halfway leaves the bot registered without a picture. Adds `provision preflight`: spec, avatar, OpenBao reachability, credentials, session presence, salt and the resulting plan, checked in one run that writes nothing and never contacts Telegram. Every failure it reports is one that would otherwise surface after a phone number had been spent. Two bugs it found immediately. The avatar path is documented as repo-relative but resolved against the spec's own directory, so the real campaign spec failed to find its own asset. And the OpenBao error named both variables when only one was missing, sending the reader to check the one already set. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0172sgCZEEDJcnQmr4SGDvKa Assistant: claude-code Assistant-Model: opus Assistant-Process: 1361245@bnt-lap001 Assistant-Session: b3b428ef-f3e6-4688-b091-01f71461d66a
This commit is contained in:
parent
014ee5c746
commit
7347bd6302
11 changed files with 661 additions and 46 deletions
|
|
@ -50,7 +50,7 @@ func Run(ctx context.Context, p *plan.Plan, o Options) error {
|
|||
if err := ensureSalt(ctx, o); err != nil {
|
||||
return err
|
||||
}
|
||||
botUser, err := ensureBot(ctx, o)
|
||||
botUser, err := ensureBot(ctx, o, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -85,10 +85,17 @@ func ensureSalt(ctx context.Context, o Options) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func ensureBot(ctx context.Context, o Options) (tg.InputUserClass, error) {
|
||||
func ensureBot(ctx context.Context, o Options, p *plan.Plan) (tg.InputUserClass, error) {
|
||||
if o.State.Bot.ID != 0 {
|
||||
fmt.Fprintf(o.Out, " ok bot @%s\n", o.State.Bot.Username)
|
||||
return resolveBot(ctx, o, o.State.Bot.Username)
|
||||
user, err := resolveBot(ctx, o, o.State.Bot.Username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := ensureAvatar(ctx, o, p, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
conv, err := o.Client.BotFather(ctx)
|
||||
|
|
@ -136,6 +143,10 @@ func ensureBot(ctx context.Context, o Options) (tg.InputUserClass, error) {
|
|||
}
|
||||
fmt.Fprintln(o.Out, " set bot about text and description")
|
||||
|
||||
if err := ensureAvatar(ctx, o, p, conv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user, err := resolveBot(ctx, o, username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -143,6 +154,27 @@ func ensureBot(ctx context.Context, o Options) (tg.InputUserClass, error) {
|
|||
return user, state.Save(o.StatePath, o.State)
|
||||
}
|
||||
|
||||
// ensureAvatar sends the picture when the plan found one that differs from what
|
||||
// is recorded. The digest is written only after BotFather confirms, so a failed
|
||||
// upload is retried next run rather than being remembered as done.
|
||||
func ensureAvatar(ctx context.Context, o Options, p *plan.Plan, conv *tgc.Conversation) error {
|
||||
if p.Avatar == nil || o.State.Bot.AvatarDigest == p.Avatar.Digest {
|
||||
return nil
|
||||
}
|
||||
if conv == nil {
|
||||
var err error
|
||||
if conv, err = o.Client.BotFather(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := conv.SetAvatar(ctx, o.State.Bot.Username, p.Avatar.Path); err != nil {
|
||||
return err
|
||||
}
|
||||
o.State.Bot.AvatarDigest = p.Avatar.Digest
|
||||
fmt.Fprintf(o.Out, " set bot picture from %s\n", p.Avatar.Path)
|
||||
return state.Save(o.StatePath, o.State)
|
||||
}
|
||||
|
||||
func resolveBot(ctx context.Context, o Options, username string) (tg.InputUserClass, error) {
|
||||
res, err := o.Client.API().ContactsResolveUsername(ctx, &tg.ContactsResolveUsernameRequest{
|
||||
Username: username,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue