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 &gt; 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  }