github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/externals/proof_service_hackernews.go (about) 1 // Copyright 2015 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package externals 5 6 import ( 7 "regexp" 8 "strings" 9 10 libkb "github.com/keybase/client/go/libkb" 11 keybase1 "github.com/keybase/client/go/protocol/keybase1" 12 jsonw "github.com/keybase/go-jsonw" 13 ) 14 15 // ============================================================================= 16 // HackerNews 17 // 18 19 type HackerNewsChecker struct { 20 proof libkb.RemoteProofChainLink 21 } 22 23 var _ libkb.ProofChecker = (*HackerNewsChecker)(nil) 24 25 func (h *HackerNewsChecker) GetTorError() libkb.ProofError { return nil } 26 27 func NewHackerNewsChecker(p libkb.RemoteProofChainLink) (*HackerNewsChecker, libkb.ProofError) { 28 return &HackerNewsChecker{p}, nil 29 } 30 31 func (h *HackerNewsChecker) CheckStatus(mctx libkb.MetaContext, hint libkb.SigHint, _ libkb.ProofCheckerMode, 32 pvlU keybase1.MerkleStoreEntry) (*libkb.SigHint, libkb.ProofError) { 33 // TODO CORE-8951 see if we can populate verifiedHint with anything useful. 34 return nil, CheckProofPvl(mctx, keybase1.ProofType_HACKERNEWS, h.proof, hint, pvlU) 35 } 36 37 // ============================================================================= 38 39 func APIBase(un string) string { 40 return "https://hacker-news.firebaseio.com/v0/user/" + un 41 } 42 43 func KarmaURL(un string) string { 44 return APIBase(un) + "/karma.json" 45 } 46 47 func CheckKarma(mctx libkb.MetaContext, un string) (int, error) { 48 u := KarmaURL(un) 49 res, err := mctx.G().GetExternalAPI().Get(mctx, libkb.APIArg{Endpoint: u}) 50 if err != nil { 51 return 0, libkb.XapiError(err, u) 52 } 53 return res.Body.GetInt() 54 } 55 56 // ============================================================================= 57 58 type HackerNewsServiceType struct{ libkb.BaseServiceType } 59 60 func (t *HackerNewsServiceType) Key() string { return t.GetTypeName() } 61 62 var hackerNewsUsernameRegexp = regexp.MustCompile(`^(?i:[a-z0-9_-]{2,15})$`) 63 64 func (t *HackerNewsServiceType) NormalizeUsername(s string) (string, error) { 65 if !hackerNewsUsernameRegexp.MatchString(s) { 66 return "", libkb.NewBadUsernameError(s) 67 } 68 // HackerNews names are case-sensitive 69 return s, nil 70 } 71 72 func (t *HackerNewsServiceType) NormalizeRemoteName(mctx libkb.MetaContext, s string) (string, error) { 73 // Allow a leading '@'. 74 s = strings.TrimPrefix(s, "@") 75 return t.NormalizeUsername(s) 76 } 77 78 func (t *HackerNewsServiceType) GetPrompt() string { 79 return "Your username on HackerNews (**case-sensitive**)" 80 } 81 82 func (t *HackerNewsServiceType) ToServiceJSON(un string) *jsonw.Wrapper { 83 return t.BaseToServiceJSON(t, un) 84 } 85 86 func (t *HackerNewsServiceType) PostInstructions(un string) *libkb.Markup { 87 return libkb.FmtMarkup(`Please edit your HackerNews profile to contain the 88 following text. Click here: https://news.ycombinator.com/user?id=` + un) 89 } 90 91 func (t *HackerNewsServiceType) DisplayName() string { return "Hacker News" } 92 func (t *HackerNewsServiceType) GetTypeName() string { return "hackernews" } 93 func (t *HackerNewsServiceType) PickerSubtext() string { return "news.ycombinator.com" } 94 95 func (t *HackerNewsServiceType) RecheckProofPosting(tryNumber int, status keybase1.ProofStatus, _ string) (warning *libkb.Markup, err error) { 96 warning = libkb.FmtMarkup(`<p>We couldn't find a posted proof...<strong>yet</strong></p>`) 97 if tryNumber < 3 { 98 warning.Append(`<p>HackerNews's API is slow to update, so be patient...try again?</p>`) 99 } else { 100 warning.Append(`<p>We'll keep trying and let you know.</p>`) 101 err = libkb.WaitForItError{} 102 } 103 return 104 } 105 func (t *HackerNewsServiceType) GetProofType() string { return t.BaseGetProofType(t) } 106 107 func (t *HackerNewsServiceType) CheckProofText(text string, id keybase1.SigID, sig string) (err error) { 108 return t.BaseCheckProofForURL(text, id) 109 } 110 111 func (t *HackerNewsServiceType) PreProofCheck(mctx libkb.MetaContext, un string) (markup *libkb.Markup, err error) { 112 if _, e := CheckKarma(mctx, un); e != nil { 113 markup = libkb.FmtMarkup(` 114 <p><strong>ATTENTION</strong>: HackerNews only publishes users to their API who 115 have <strong>karma > 1</strong>.</p> 116 <p>Your account <strong>` + un + `</strong> doesn't qualify or doesn't exist.</p>`) 117 mctx.Debug("Error from HN: %s", e) 118 err = libkb.NewInsufficientKarmaError(un) 119 } 120 return 121 } 122 123 func (t *HackerNewsServiceType) MakeProofChecker(l libkb.RemoteProofChainLink) libkb.ProofChecker { 124 return &HackerNewsChecker{l} 125 }