github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/chat/utils/utils_test.go (about) 1 package utils 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "regexp" 8 "testing" 9 "time" 10 11 "github.com/keybase/client/go/chat/globals" 12 "github.com/keybase/client/go/chat/types" 13 "github.com/keybase/client/go/externalstest" 14 "github.com/keybase/client/go/libkb" 15 "github.com/keybase/client/go/protocol/chat1" 16 "github.com/keybase/client/go/protocol/gregor1" 17 "github.com/keybase/client/go/protocol/keybase1" 18 19 "github.com/stretchr/testify/require" 20 ) 21 22 func TestParseDurationExtended(t *testing.T) { 23 test := func(input string, expected time.Duration) { 24 d, err := ParseDurationExtended(input) 25 if err != nil { 26 t.Fatal(err) 27 } 28 if d != expected { 29 t.Fatalf("wrong parsed duration. Expected %v, got %v\n", expected, d) 30 } 31 } 32 test("1d", time.Hour*24) 33 test("123d12h2ns", 123*24*time.Hour+12*time.Hour+2*time.Nanosecond) 34 } 35 36 func TestParseAtMentionsNames(t *testing.T) { 37 text := "@Chat_1e2263952c hello! @Mike From @chat_5511c5e0ce. @ksjdskj 889@ds8 @_dskdjs @k1 @0011_" 38 matches := parseRegexpNames(context.TODO(), text, atMentionRegExp) 39 var names, normalizedNames []string 40 for _, m := range matches { 41 names = append(names, m.name) 42 normalizedNames = append(normalizedNames, m.normalizedName) 43 } 44 45 expected := []string{"Chat_1e2263952c", "Mike", "chat_5511c5e0ce", "ksjdskj", "k1", "0011_"} 46 require.Equal(t, expected, names) 47 expectedNormalized := []string{"chat_1e2263952c", "mike", "chat_5511c5e0ce", "ksjdskj", "k1", "0011_"} 48 require.Equal(t, expectedNormalized, normalizedNames) 49 text = "@mike@jim" 50 matches = parseRegexpNames(context.TODO(), text, atMentionRegExp) 51 names = []string{} 52 for _, m := range matches { 53 names = append(names, m.name) 54 } 55 expected = []string{"mike"} 56 require.Equal(t, expected, names) 57 } 58 59 type testTeamChannelSource struct { 60 channels []string 61 } 62 63 var _ types.TeamChannelSource = (*testTeamChannelSource)(nil) 64 65 func newTestTeamChannelSource(channels []string) *testTeamChannelSource { 66 return &testTeamChannelSource{ 67 channels: channels, 68 } 69 } 70 71 func (t *testTeamChannelSource) GetChannelsTopicName(ctx context.Context, uid gregor1.UID, 72 teamID chat1.TLFID, topicType chat1.TopicType) (res []chat1.ChannelNameMention, err error) { 73 for _, c := range t.channels { 74 res = append(res, chat1.ChannelNameMention{ 75 TopicName: c, 76 }) 77 } 78 return res, nil 79 } 80 81 func (t *testTeamChannelSource) GetLastActiveForTLF(ctx context.Context, uid gregor1.UID, tlfID chat1.TLFID, 82 topicType chat1.TopicType) (gregor1.Time, error) { 83 return 0, fmt.Errorf("testTeamChannelSource.GetLastActiveForTLF not implemented") 84 } 85 86 func (t *testTeamChannelSource) GetLastActiveForTeams(ctx context.Context, uid gregor1.UID, 87 topicType chat1.TopicType) (res chat1.LastActiveTimeAll, err error) { 88 return res, fmt.Errorf("testTeamChannelSource.GetLastActiveForTeams not implemented") 89 } 90 91 func (t *testTeamChannelSource) GetChannelTopicName(ctx context.Context, uid gregor1.UID, 92 teamID chat1.TLFID, topicType chat1.TopicType, convID chat1.ConversationID) (string, error) { 93 return "", fmt.Errorf("testTeamChannelSource.GetChannelTopicName not implemented") 94 } 95 96 func (t *testTeamChannelSource) GetChannelsFull(ctx context.Context, uid gregor1.UID, 97 teamID chat1.TLFID, topicType chat1.TopicType) (res []chat1.ConversationLocal, err error) { 98 return res, nil 99 } 100 101 func (t *testTeamChannelSource) GetRecentJoins(ctx context.Context, convID chat1.ConversationID, remoteClient chat1.RemoteInterface) (int, error) { 102 return 0, nil 103 } 104 105 func (t *testTeamChannelSource) GetLastActiveAt(ctx context.Context, teamID keybase1.TeamID, uid gregor1.UID, remoteClient chat1.RemoteInterface) (gregor1.Time, error) { 106 return 0, nil 107 } 108 109 func (t *testTeamChannelSource) OnDbNuke(mctx libkb.MetaContext) error { 110 return nil 111 } 112 113 func (t *testTeamChannelSource) OnLogout(mctx libkb.MetaContext) error { 114 return nil 115 } 116 117 func TestParseChannelNameMentions(t *testing.T) { 118 uid := gregor1.UID{0} 119 teamID := chat1.TLFID{0} 120 chans := []string{"general", "random", "miketime"} 121 text := "#miketime is secret. #general has everyone. #random exists. #offtopic does not." 122 matches := ParseChannelNameMentions(context.TODO(), text, uid, teamID, 123 newTestTeamChannelSource(chans)) 124 expected := []chat1.ChannelNameMention{ 125 {TopicName: "miketime"}, 126 {TopicName: "general"}, 127 {TopicName: "random"}, 128 } 129 require.Equal(t, expected, matches) 130 } 131 132 type testUIDSource struct { 133 libkb.UPAKLoader 134 users map[string]keybase1.UID 135 } 136 137 func newTestUIDSource() *testUIDSource { 138 return &testUIDSource{ 139 users: make(map[string]keybase1.UID), 140 } 141 } 142 143 type testInboxSource struct { 144 types.InboxSource 145 } 146 147 func (t testInboxSource) Read(ctx context.Context, uid gregor1.UID, localizeTyp types.ConversationLocalizerTyp, 148 dataSource types.InboxSourceDataSourceTyp, maxLocalize *int, query *chat1.GetInboxLocalQuery) (types.Inbox, chan types.AsyncInboxResult, error) { 149 return types.Inbox{ 150 Convs: []chat1.ConversationLocal{{ 151 Info: chat1.ConversationInfoLocal{ 152 TopicName: "mike", 153 }, 154 }, 155 }}, nil, nil 156 } 157 158 func (s *testUIDSource) LookupUID(ctx context.Context, un libkb.NormalizedUsername) (uid keybase1.UID, err error) { 159 var ok bool 160 if uid, ok = s.users[un.String()]; ok { 161 return uid, nil 162 } 163 return uid, errors.New("invalid username") 164 } 165 166 func (s *testUIDSource) AddUser(username string, uid gregor1.UID) { 167 s.users[username] = keybase1.UID(uid.String()) 168 } 169 170 func TestSystemMessageMentions(t *testing.T) { 171 tc := externalstest.SetupTest(t, "chat-utils", 0) 172 defer tc.Cleanup() 173 174 g := globals.NewContext(tc.G, &globals.ChatContext{InboxSource: testInboxSource{}}) 175 // test all the system message types gives us the right mentions 176 u1 := gregor1.UID([]byte{4, 5, 6}) 177 u2 := gregor1.UID([]byte{4, 5, 7}) 178 u3 := gregor1.UID([]byte{4, 5, 8}) 179 u1name := "mike" 180 u2name := "lisa" 181 u3name := "sara" 182 usource := newTestUIDSource() 183 usource.AddUser(u1name, u1) 184 usource.AddUser(u2name, u2) 185 usource.AddUser(u3name, u3) 186 tc.G.SetUPAKLoader(usource) 187 body := chat1.NewMessageSystemWithAddedtoteam(chat1.MessageSystemAddedToTeam{ 188 Adder: u1name, 189 Addee: u2name, 190 }) 191 atMentions, chanMention, _ := SystemMessageMentions(context.TODO(), g, u1, body) 192 require.Equal(t, 1, len(atMentions)) 193 require.Equal(t, u2, atMentions[0]) 194 require.Equal(t, chat1.ChannelMention_NONE, chanMention) 195 body = chat1.NewMessageSystemWithInviteaddedtoteam(chat1.MessageSystemInviteAddedToTeam{ 196 Invitee: u3name, 197 Inviter: u1name, 198 Adder: u2name, 199 }) 200 atMentions, chanMention, _ = SystemMessageMentions(context.TODO(), g, u1, body) 201 require.Equal(t, 2, len(atMentions)) 202 require.Equal(t, u1, atMentions[0]) 203 require.Equal(t, u3, atMentions[1]) 204 require.Equal(t, chat1.ChannelMention_NONE, chanMention) 205 body = chat1.NewMessageSystemWithComplexteam(chat1.MessageSystemComplexTeam{ 206 Team: "MIKE", 207 }) 208 atMentions, chanMention, _ = SystemMessageMentions(context.TODO(), g, u1, body) 209 require.Zero(t, len(atMentions)) 210 require.Equal(t, chat1.ChannelMention_ALL, chanMention) 211 212 body = chat1.NewMessageSystemWithNewchannel(chat1.MessageSystemNewChannel{}) 213 atMentions, chanMention, channelNameMentions := SystemMessageMentions(context.TODO(), g, u1, body) 214 require.Zero(t, len(atMentions)) 215 require.Equal(t, chat1.ChannelMention_NONE, chanMention) 216 require.Equal(t, 1, len(channelNameMentions)) 217 require.Equal(t, "mike", channelNameMentions[0].TopicName) 218 } 219 220 func TestFormatVideoDuration(t *testing.T) { 221 testCase := func(ms int, expected string) { 222 require.Equal(t, expected, formatVideoDuration(ms)) 223 } 224 testCase(1000, "0:01") 225 testCase(10000, "0:10") 226 testCase(60000, "1:00") 227 testCase(60001, "1:00") 228 testCase(72000, "1:12") 229 testCase(3600000, "1:00:00") 230 testCase(4500000, "1:15:00") 231 testCase(4536000, "1:15:36") 232 testCase(3906000, "1:05:06") 233 } 234 235 func TestGetQueryRe(t *testing.T) { 236 queries := []string{ 237 "foo", 238 "foo bar", 239 "foo bar, baz? :+1:", 240 } 241 expectedRe := []string{ 242 "foo", 243 "foo bar", 244 "foo bar, baz\\? :\\+1:", 245 } 246 for i, query := range queries { 247 re, err := GetQueryRe(query) 248 require.NoError(t, err) 249 expected := regexp.MustCompile("(?i)" + expectedRe[i]) 250 require.Equal(t, expected, re) 251 t.Logf("query: %v, expectedRe: %v, re: %v", query, expectedRe, re) 252 ok := re.MatchString(query) 253 require.True(t, ok) 254 } 255 } 256 257 type decorateMentionTest struct { 258 body string 259 atMentions []string 260 chanMention chat1.ChannelMention 261 channelNameMentions []chat1.ChannelNameMention 262 result string 263 } 264 265 func TestDecorateMentions(t *testing.T) { 266 convID := chat1.ConversationID([]byte{1, 2, 3, 4}) 267 cases := []decorateMentionTest{ 268 { 269 body: "@mikem fix something", 270 atMentions: []string{"mikem"}, 271 // {"typ":1,"atmention":"mikem"} 272 result: "$>kb$eyJ0eXAiOjEsImF0bWVudGlvbiI6Im1pa2VtIn0=$<kb$ fix something", 273 }, 274 { 275 body: "@Mikem,@Max @mikem/@max please check out #general, also @here you should too", 276 atMentions: []string{"mikem", "max"}, 277 chanMention: chat1.ChannelMention_HERE, 278 channelNameMentions: []chat1.ChannelNameMention{{ 279 ConvID: convID, 280 TopicName: "general", 281 }}, 282 // {"typ":1,"atmention":"Mikem"} 283 // {"typ":1,"atmention":"Max"} 284 // {"typ":1,"atmention":"mikem"} 285 // {"typ":1,"atmention":"max"} 286 // {"typ":2,"channelnamemention":{"name":"general","convID":"01020304"}} 287 // {"typ":1,"atmention":"here"} 288 result: "$>kb$eyJ0eXAiOjEsImF0bWVudGlvbiI6Ik1pa2VtIn0=$<kb$,$>kb$eyJ0eXAiOjEsImF0bWVudGlvbiI6Ik1heCJ9$<kb$ $>kb$eyJ0eXAiOjEsImF0bWVudGlvbiI6Im1pa2VtIn0=$<kb$/$>kb$eyJ0eXAiOjEsImF0bWVudGlvbiI6Im1heCJ9$<kb$ please check out $>kb$eyJ0eXAiOjIsImNoYW5uZWxuYW1lbWVudGlvbiI6eyJuYW1lIjoiZ2VuZXJhbCIsImNvbnZJRCI6IjAxMDIwMzA0In19$<kb$, also $>kb$eyJ0eXAiOjEsImF0bWVudGlvbiI6ImhlcmUifQ==$<kb$ you should too", 289 }, 290 { 291 body: "@mikem talk to @patrick", 292 atMentions: []string{"mikem"}, 293 result: "$>kb$eyJ0eXAiOjEsImF0bWVudGlvbiI6Im1pa2VtIn0=$<kb$ talk to @patrick", 294 }, 295 { 296 body: "see #general", 297 result: "see #general", 298 }, 299 { 300 body: "@here what are you doing!", 301 result: "@here what are you doing!", 302 }, 303 { 304 body: `\@mikem,\@max \@mikem/\@max please check out \#general, also \@here you should too`, 305 atMentions: []string{"mikem", "max"}, 306 chanMention: chat1.ChannelMention_HERE, 307 channelNameMentions: []chat1.ChannelNameMention{{ 308 ConvID: convID, 309 TopicName: "general", 310 }}, 311 result: `\@mikem,\@max \@mikem/\@max please check out \#general, also \@here you should too`, 312 }, 313 } 314 for _, c := range cases { 315 res := DecorateWithMentions(context.TODO(), c.body, c.atMentions, nil, c.chanMention, 316 c.channelNameMentions) 317 require.Equal(t, c.result, res) 318 } 319 } 320 321 func BenchmarkDecorateLinks(b *testing.B) { 322 var messages = []string{ 323 "The buttons have been \"encrypted\" and the plaintext is still there.", 324 ":joy: ", 325 "it looked like \"CASINO\" to me ", 326 "I like it, and I think that if you look at it closely you can see where it has \"CRYPTO\" almost hidden in the _charaters_. Same thing for the Try it button in the what's new.", 327 "Actually I like it!", 328 "Maybe a checkbox in settings to keep it if you want to?", 329 "As in, once you click on the tab, the text stays normal", 330 "I do think it's cool. but it should be a one-time thing", 331 "then I realized it was a clever thing and embraced its quirkiness :D", 332 "I thought it was a corrupted system font or something :P ", 333 "I didn't see that label in the announce blog entry on the feature. If it's not there, I'd surmise it to be a bug.", 334 "I first thought it was in hebrew or something", 335 "I hope they'll remove the weird label, it's murdering my OCD", 336 "Oh really?! Oh.. that’s a shame... :( ", 337 "The program ended in December and was killed off by the influx of spammers and thieves. No January airdrop. There's a team for it #stellar where they will tell you the same (and worse) heh @rottentweetie Also search for a team called airdrop for more info and misc spammers galore.", 338 "there is none in january. it ended. would be simple to google @rottentweetie ", 339 "Hi there! How are ya’ll?? Question: I didn’t recieve the airdrop of lumens of januari. Did you guys do?! Or the same as I? ", 340 "I am currently trying the \"linux\" path so far it has bot crashed", 341 "Lovelly getting error saying that path from windows is not a KBFS path", 342 "you should be able to find out the exact path from your windows explorer portion though", 343 "i am not sure about windows. for linux/mac which i use it's /keybase/private/smms in my case. ", 344 "go to https://keybase.io/docs/kbfs/understanding_kbfs ; under `Time travel` portion, it explains in details about how you gonna restore", 345 "or something else? I am on windows", 346 "would my path be K:/public/idah6?", 347 ":sweat_smile: ugh no...", 348 "The hero has arrived. :tada:", 349 "@idah6 go to console and `keybase fs stat`, it has the commands you need", 350 "thanks for the help though", 351 "you can restore, `keybase fs stat --show-archived <your_kbfs_path> `to check on the revision", 352 "@chindraba yeah this was not something I had planned for, I kept checking before performing any operations, but whatever, this happens every now and then", 353 "I don't know for sure, and don't know how to do it, but I think you have some hope. Be patient as this is sort of the dead time for here.", 354 "Something about the merkle tree. ", 355 "I think the files are stored in a manner that prior versions can be seen/copied/recoverd. Similar to git.", 356 "@idah6 I think there is. I don't know it, but I don know one thing: STOP. Do nothing further until you have the answer. To continue could ruin the chance of recovery.", 357 "I need help, I accidently was deleting files off of Keybase, and was asked to permanently deleting them (thinking it was the local copy in another location on the computer) I stopped the operation but is there any way of restoring those files?", 358 "Type an @ and the first few letters, click the one you want and then copy that.", 359 "Never had it self-activate though. And I very seldom close the main window anyway. It's set to show on all desktops, so I don't have to even look at the tray icon.", 360 "@nevezen I think, now that you describe it, that I've seen that. When I click on the KB icon in the tray, it show \"Show Keybase\" which opens that window. I've always just clicked on one of the chats, or the chat icon on top, as that's where I'm going then anyway.", 361 "why it need to copy a user's name btw?", 362 "MacOS, btw.", 363 "Hi. I love keybase, but I just had a really frustrating moment trying to copy a user's name. Ultimately I couldn't figure out a way to do it heh.", 364 "Ah ok, 👌🏼", 365 "\"Fast\"er than logging out user one and logging in with user two.", 366 "Windows feature which allows multiple users to be \"logged in\" at the same time, even though only one is able to use the system at once.", 367 "“Switch user” from the start menu", 368 "What do u mean by fast uset switching ?", 369 "More like a quick access contextual menu launched from the notifications bar icon.", 370 "Has icons for people, chat, files, teams, hamburger and a window list of recent chats and files.", 371 "Right now I’m just testing on a win 10 desktop with fast user switching. Tomorrow I’ll try on a true multi user server. ", 372 "Does the first user, on K:, loose access to their files on K: when the second user has X: open?", 373 "OK testing this... gone back and forth mis-interpreting results but... looks like first user to connect gets K:, second gets X:", 374 "Have not seen that happen, yet. Is it a new window for each conversation, or just a new window which handles all conversations?", 375 "The same with the try it! button on keybase fm. Looks intentional", 376 "ugh, keybase now shows a dedicated mini-window for messages?", 377 ":)", 378 "Mouse over", 379 "I think intentional", 380 "Only one Windows user at a time?", 381 "Gak! Is this still true?", 382 "meanwhile, my kbfs is stuck", 383 "I had to join, and then turn off notifications ", 384 "Yeah....", 385 "It is", 386 "Is that the spacedrop replay?", 387 "why is keybase sending me notifications for a channel *I'm not even in* ugh", 388 "good point. I was assuming this would be the case.", 389 "So there will be some system storage usage ", 390 "Don’t forget though that kbfs requires local scratch space to encrypt blocks before they’re sent to kbfs", 391 "No worries", 392 "You're welcome.", 393 "NP, didn't do anything anyway.", 394 "Thank you @smms, @chindraba, @stefan_claas for the input. ", 395 "@alphydan maybe worth to take a look at GiHub and ask there. https://github.com/keybase/client/issues", 396 "yeah, files stored on kbfs won't use your local storage. it's under kbfs storage quota. you could check by `keybase fs quota` ; simplest way is to think of it as a network mounted disk", 397 "so if I `cp some-file /keybase/team/some-team/some-file` it doesn't occupy any space in the local machine?", 398 "You could just save the files at your keybase/private or public folder path. no need special command. As long as your kbfs is mounted, it's a non issue saving there ", 399 "(a use case is that I only have 2GB of space left on the server, but would like to store 4GB on kbfs, and whether it can be done programmatically) ", 400 "my question is whether it is possible to save a file to the kbfs (in a server, using shell commands or the API) without using local storage ", 401 "As I understand it, here. Answers might be ymmv styles though ", 402 "Family voice mail is rare. Email most likey, text possibly. Most times when it's a missed call no messages are left.", 403 "Where is the best place to ask some technical questions about the keybase api? ", 404 "It's kindof annoying but also cute. I listen to them once in a while when i miss her", 405 "I got too many from my grandmother saying something like: > Hey it's me [name]. I just wanted to hear how everything is going. you don't have to call back it's always the same. tried to say that it's nice and all, but if she only leaves a message when it is something out of the ordinary then i'll listen to them at that point. but nah", 406 "I'm also not dissing people using it as a communication-of-choice, if it is consentual. I'm just, myself, trying to be less wired in because it fucks with your brain", 407 "I don't even listen to phone voice mail except for family and doctors ", 408 "By phone, voip, even voice chat like discord. But, not messages or passing thoughts and notes.", 409 "When I use voice it's for a conversation, not messages in a bottle.", 410 "I'm all for new tech and shit. and evolution of society and shit. And obviously phones have done a lot of good. But is your life really that much of an action movie that you can't either pull into the side of the road or wait ? I mean we used to have to go home to use the phone. again.. not a \"new-tech-bad\". just.. people could chill", 411 "Voice msg or not.. is personal preference, no real justification to remove it. it's similar to request ability to send exloding message in group removed, and exploding msg should be available in 1/1 convo only. ", 412 "😹 Yeah. I don't even *listen* to or accept audio messages or voice calls from anyone I don't care about.", 413 "https://keybase.io/docs/teams/design", 414 "subteams and their parent teams and other subteams under the same parent are all separate and can not see files or messages from one another. however, they'll all share the quota of the parent team (100gb total across all sub teams), and an admin/owner of a team can add themself to any subteam of the team they're admin/owner of", 415 "😆 ", 416 "It annoys me when my wife does it, but she gets a pass because I’m married to her. You guys, on the other hand....", 417 `Right, and that’s exactly it. It places the burden on the listener. And this might be crazy, but hear me out: 418 - I get that you’re busy or on the go, but that doesn’t mean I’m not 419 - I get that sometimes you can’t type, like when you’re driving. Maybe you should wait? I’m probably not in a position that I can listen all the time. 420 421 I feel like it proxies the burden, and (unfortunately and unintentionally) makes an implicit statement that their time is more important than your time right now. `, 422 "If you're in group with peeps on the go it's easier for them to just record, but cumbersome to listen.", 423 "I get that some people like them, but I’d prefer if my client deleted them and auto responded “ain’t got time for that”", 424 "Making voice messages go away would be an equally acceptable solution 😆 ", 425 "Super bowl ", 426 "!en 슈퍼 볼 ", 427 "Superbowl ", 428 "!tl Superbowl", 429 "슈퍼 볼 ", 430 "!ko Superbowl", 431 "Hi, new here", 432 "오늘은 일요일입니다 ", 433 "!ko today is sunday", 434 "이것은 어느 봇입니까? ", 435 "!ko Which bot is this?", 436 "a bot that converts voice messages into text would also be very helpful. Voicy does it for Telegram.", 437 "I made the webooks work, wee", 438 "How does access to files belonging to teams work? Do members of subteams automatically have access to files of parents? Or do members of teams have access to files of subteams? Or none of the two?", 439 "would be nice if they made this page work with saltpack https://keybase.io/verify", 440 "thanks for finally implementing the direct messages \"blocks\" and moderation", 441 "Has anyone has any luck with incoming webhooks, and has some examples? I havent been able to find any doc for it", 442 ":wave:", 443 "Hey all :)", 444 "Welcome", 445 "Thanks!", 446 "@greenarmor Here's an impressive list. https://awesomeopensource.com/projects/text-to-speech", 447 "Might not be as good as Google, but run locally, much more private.", 448 "Probably even OpenSource.", 449 "None I know, but I suspect there are some TTS programs available.", 450 "any alternative you can think of other than using Gtts?", 451 "not wise but sound cool", 452 ":wave:", 453 "@greenarmor is that wise? The transcript going from an E2E secure channel into Google for TTS?", 454 "using google tts", 455 "im working on a bot recording minutes of a meeting inside a team then after the meeting, bot can send back the minutes as audio file", 456 "Learning curve for everyone. Huge teams, like this one, are bound to have more than a few complications.", 457 "actaully theres 2 triggers same with @sholebot the !price. i already changed", 458 "i cant changed mine the korean team used to it", 459 "Yep, imagine single word triggers 10+ bots info. ", 460 "Cute, for a puppy.", 461 "!eyebleach", 462 "Lesson to be had if you make your own bot :D", 463 "Ah ok", 464 "the help command was responded to by both bots", 465 "Some keyword triggered it?", 466 "How did this ssh0le bot suddenly appear?", 467 "!urban whelp", 468 "!cat", 469 "!help", 470 "Nada.", 471 "뭐야 ", 472 "All the work, all the blame, and none of the credit.", 473 "!ko what's up", 474 "No wonder they want to revolt.", 475 "lol", 476 "Sure.. blame the bot.", 477 "his fault :P", 478 "Syntactically probably more correct, however.", 479 "its google translate", 480 "That time it droped one of the commas.", 481 "좋은 아침, 축복받은 날 러시아 친구. ", 482 "!ko Good morning and blessed day my Russian friend.", 483 "Something's lost in the translation.", 484 "Good morning, blessed day, Russian friend. ", 485 "!en 좋은 아침, 축복받은 날, 러시아 친구.", 486 "im not sure", 487 "Does Korean lack conjuctions?", 488 "좋은 아침, 축복받은 날, 러시아 친구. ", 489 "!ko Доброе утро и благословенный день, мой русский друг.", 490 "어떻게 지내? ", 491 "!ko how are you?", 492 "!kor Доброе утро и благословенный день, мой русский друг.", 493 "Use en as the input for korean and tagalog?", 494 "all ready in", 495 "all major languages to english", 496 "So, the bot reads lots of stuff. only speaks the three?", 497 "Philippines", 498 "I don't even know what, or where from, tagalog is.", 499 "Good morning and blessed day, my Russian friend. ", 500 "!en Buenos días y bendito día, mi amigo ruso.", 501 "Buenos días y bendito día, mi amigo ruso.", 502 "i just add korean ang tagalog", 503 "Why? didn't teach the bot Spanish?", 504 "wont work", 505 "!es Доброе утро и благословенный день, мой русский друг.", 506 "ahaha", 507 "Make a bot do all your work for you.", 508 "cheating ?", 509 "Nice trick, even if it is _cheating_.", 510 "Good morning and blessed day, my Russian friend. ", 511 "!en Доброе утро и благословенный день, мой русский друг.", 512 "Доброе утро и благословенный день, мой русский друг.", 513 "Good morning from fairy cold Russia ", 514 ":wave:", 515 ":wave:", 516 "Mmm", 517 "I think it worked on block quotes too.", 518 "Something like \"see full text\".", 519 "I've seen someplace where code blocks were limited to a few lines, and reading more required a click, and it reset if you left the room and came back. Not sure where, so I can't demo it. )-:", 520 "the real feature request is always in the comments", 521 "Or, if the other recepiant has copied it, perhaps the sender could delete it. ", 522 "Or maybe allow us to collapse code blocks", 523 "Wow feature request: hide message", 524 "cool thanks @dxb ", 525 "Dang, that's a boat-load of text for a short message.", 526 "pm @b07 with `!help`, check out #discovery-channel, and check out keybase.io/popular-teams", 527 "Is there anyway to search for big chat rooms on keybase?", 528 "Well, that's a possible problem", 529 "No cli of their own", 530 "My mobiles only ssh to the desktop.", 531 "maybe make sure there's actually something on your clipboard? :p", 532 "Can't do that here. Only my desktop is usable for CLI", 533 "i tested it with `termux-clipboard-get` and it worked though", 534 "Perhaps the termux-clipboard-get isn't doing it correctly. Or something on the choosen options aren't set right.", 535 "So it seems, Works for me with various options and no --message/-m flag", 536 "you're right though, that's standard, and i'm sure it works that way too", 537 "you don't need `-m` at all when piping to it", 538 "The `-m` needs the clear text on the command line. To make pipe work you need to use `-` as a the \"message\". `termux-clipboard-get | keybase encrypt -m - <user>`", 539 "also, since your `&` was outside of the code tag on your message i assume that wasn't part of it right?", 540 "try something simple first. `termux-clipboard-get | keybase encrypt <user>` (don't use -m when piping to stdin)", 541 "works fine for me… are you encrypting for a team or some ridiculously large group?", 542 "I tried with that param and without and it wouldn't read my piped input", 543 "are you using the `-m` option?", 544 " I'm curious; I'm trying to use `keybase encrypt` from the cli (as the feature isn't on mobile *yet*) and I'm unable to get keybase to read my `stdin` input. I'm piping the input in like so `termux-clipboard-get | keybase encrypt <params> <target> <flags>` & this isn't working", 545 "ttfn", 546 "Uncle did, August Dvorak", 547 "In college I was proficient equally with that and QWERTY.", 548 "Loved his keyboard, but don't use it anymore.", 549 "Never hear a single one, nor read anyting from him.", 550 "Dvorak", 551 "Which he never claimed anyway.", 552 "I was \"online\" years before Gore \"invented\" the Internet", 553 "10 characters per second print speed. No \"screen\" just paper.", 554 "Teletype Model 32 ASR connected to 110 baud dial-up.", 555 "Don't recall what my first computer was, but I remember it being upgraded to an UNIVAC 1110", 556 "Not my first computer. My first IGM-family computer.", 557 "First build was PC-XT clone, 20 MB HDD, 640K RAM, dual 5.25 180K floppy.", 558 "Until the current box I've always built my own box.", 559 "It's probably a bit dated, but a good idea of how to work the whole thing is spelled out in one place. https://nodakengineering.com/?page_id=501", 560 "The virtual box, usable linux, and linux from scratch are all free. ", 561 "@damccull If you want to play with linux, and learn what's really going on under the hood with everything. Try the Linux From Scratch project. Of course it requires a running linux to build it, but that can be in a virtual box.", 562 "Had full-suite integration working before MSO even tried to do it.", 563 "I even got to like the extras they had, Director I think it was called.", 564 "Up to WP 12 on XP", 565 "I had WP on Win and never had an issue with it.", 566 "WP had MSO beat 7 ways to sunday. Just didn't have the draconian marketing to keep it going", 567 "I've adapted to gui, reasonably well anywya", 568 "More of a cli-fan that finds gui handy, as long as it don't bury things too deep", 569 "Haven't liked MS Office since they added the ribbon. 2003 or 2007.", 570 "I've used win 8/8.1/10 enough to know I'm never going back.", 571 "I've always had, and still have, a seldom-used boot into Win. Mostly to help others.", 572 "Haven't really \"used\" win since vista came out and made my pc look like a new mac", 573 "Haven't used Mac since pre-osx days, and then very little.", 574 "That plus MS Office is just so....above all the competitors. If MS would port office365 to linux, and I could get a legit gaming capability that didn't require any tweaking and was completely transparent and could play all mah games, I'd switch today. Those are the only things holding me back.", 575 "Do you spend significant time doing other things, in blocks of time rather than piece-meal?", 576 "I keep wanting to switch to linux but I can't seem to break away from windows as a gaming platform, and that's basically what I do all the time so... :D", 577 "I'm not. Linux now. Then I had a few installs under multi-boot. 3rd physical partition on 1st of 3 hdd installed.", 578 "Not that it should be against the rules, but why are you using g:/ as the system disk?", 579 "didn't worg so well when i was using `g:/` as the system disk.", 580 "the official update did something relative to inventoring software in `c:/program files`", 581 "Rebuilding the FAT table with debug was an experience not to be forgotten, nor repeated.", 582 "It's hard to screw up windows unless you have a hobby of trying out warez", 583 "Well, back in the old days of MSDOS I did accidentally format the wrong drive. My fault for not double checking the assignments after adding a new drive.", 584 "Surprisingly enough the only time I've had anything happen to my system was back in my Windows days and M$ had a bad-acting update.", 585 "The goal is to protect you from the software you run. Since that presumably includes a web browser, which is running JavaScript from who-knows-where, some of which may be trying to leverage runtime vulnerabilities, it's a prudent idea.", 586 "Create `/etc/synthetic.conf` with `/keybase` in it.", 587 "It's possible, just has to be configured before boot time.", 588 "That's my only beef, so far anyway, with KDE", 589 "that makes sense, thanks. bit of a bummer but all in the name of security innit", 590 "I hate it when \"my\" system tries to protect me from _myself_.", 591 "things aren't allowed to make folders in `/` anymore", 592 "https://github.com/keybase/client/issues/17835", 593 `> Keybase 4.4.0 (out today) has better file system support for Catalina now. Please let us know if you have more problems with it. Note that the /keybase mount point is no longer viable on fresh installs of Catalina due to macOS namespace restrictions, so now we mount at /Volumes/Keybase if /keybase does not already exist. 594 595 `, 596 "system integrity made it impossible", 597 "can still run fs commands thru `/keybase` via `keybase` cli tho", 598 "Linux here. Still works.", 599 "did KBFS change in an update? I can no longer cd into `/keybase` like I used to but I can access through `/Volumes/Keybase (current_user)/` (this on macOS Catalina)", 600 "Called `Kebase Key ID`", 601 "`keybase pgp list` command gives the ID", 602 "Ok I see it.", 603 "Ah...I see. Thanks for pointing that out. I thought it was inconvenient to have to log into the website ;D", 604 "then use \"keybase Key ID\"", 605 "yeah you have to do `keybase pgp list`", 606 "the ID must be keybase's internal ID for it", 607 "ah, signing into the website gives a big long string when i click 'edit' next to the key", 608 "\"Error parsing command line arguments: bad key: KID wrong length; wanted 35 but got 8 bytes\"", 609 "Nope, same error. How strange.", 610 "try using `f94da63df218aa31` as given on the profile", 611 "grr. `keybase pgp drop` keeps telling me it wants 35 bytes but only got <number less than 35> bytes. Obviously I'm using the wrong thing. What hsould I be using?", 612 "fair enough", 613 "Not sure where I got that one from.", 614 "Yep. I see two keys next to my picture but neither has that code you sent earlier.", 615 "go to your main profile page", 616 "how do you see the key id you were sending me earlier? I don't see that number anywhere.", 617 "Same thing, except how you get it into your profile.", 618 "So using a custom key or generating one is the same thing?", 619 "Oh.", 620 "Actually, they key isn't signed by Keybase. It's entry into the merkle tree is signed by your KB identity.", 621 "If i had a preexisting cert, could I have keybase sign that somehow? I see that it signs your cert when you create one through them", 622 "They use a \"gossip\" network. What one server knows, eventually they all know.", 623 "Nice.", 624 "One is all.", 625 "Hmm. Are the key servers linked to share data, or is there a popular one I should use?", 626 "Have to upload it to a regular keyserver.", 627 "That's the part keybase doesn't work with.", 628 "That way there system doesn't accidentally try to use it later", 629 "So you upload the key back to keybase with the revoked part in it?", 630 "Later, if you use the key with other people, it's nice to let them know it's revoked, and send them the revoked key to update their keyring.", 631 "3) let others know it's revoked by sending the revoked key to the keyserver", 632 "2) Actually \"revoke\" the key by importing the revocation cert into the key", 633 "1) create a revocation cert. Should have one handy for any key you make anyway.", 634 "hmm. interesting. how do you send revocations out?", 635 "Keybase, however, doesn't have a tool for revoking PGP certs. only removing them from the Keybase profile.", 636 "It was listed on your profile, so anyone could have goten it from there. Made easy on purpose.", 637 "i only see F94DA63DF218AA31 and 43E0A440A5971D1B", 638 "I've never posted any keys to a public server on my own. Unless keybase did it outside their own server, that's the only place that should exist.", 639 "Now I see `f218aa31` not sure where I got that other one from", 640 "While expirementing, try to keep copies of everything, in case you need to \"clear the record\" later.", 641 "I'm enjoying it. I don't see af971d1b on my profile...there are two keys on the page though", 642 "It's lots of fun, imho", 643 "I'm newish to pgp.", 644 "Oh yeah I added that one as a test just now lol.", 645 "Nope, now I see another one `af971d1b`", 646 "Oh, interesting. Ok, it's saying no secret key available, so perhaps I have lost that in the past.", 647 "That's the id from your profile.", 648 "The revoked key can be sent to one of the pgp key servers, and anyone trying touse it, if they got it from somewhere, will know it's revoked.", 649 "No f21...found...should I replace with my own?", 650 "That generates a revocation cert, and then imports it back into the keychain, merging them, and making the key now revoked.", 651 "Then issue a revocation certificate for it, with `gpg --gen-revoke f218aa31 | gpg --import`", 652 "Ok I did that. Now I see it in my local keychain. Doesn't look like something I recognize, though it has my name. I must have generated it as a test when keybase released back in the day.", 653 "If not, add it with `keybase pgp export --unencrypted | gpg --import`", 654 "It may, or may not, be in your computer's GPG keychain.", 655 "Well, purging it would not cause any issues. However, I'd recomend a few other steps first.", 656 "I don't know what it's for anymore, I'm getting ready to create a new one that I will know what it's for, and I don't really want it anymore. I haven't ever used it that I'm aware of.", 657 "Why do you need to remove the key?", 658 "I have a pgp key in my `keybase pgp list` and I'm not sure if I generated it in the past or if it was created automatically. Can I safely `keybase pgp purge` without screwing up my account?", 659 "Achetez-en dans un échange. Ensuite, essayez d'utiliser l'anglais dans les zones où tout le monde l'utilise. Probabilité accrue de compréhension.", 660 `Bonjour, 661 Y-a-t-il des gens parlant français ici ? Besoin d'infos sur la façon la plus simple d'acquérir des Lumens. 662 Bonne soirée. 663 `, 664 "the exchange account she mentioned seems unknowned but funded by interstellar, i dont think interstellar need a memo", 665 "Np!! ", 666 "Great! Thank you so much for this help! Much appreciated.", 667 "You have to contact your exchange, usually they will able to resolve it for you with few additional steps fr them ", 668 "What happens with the XLM if I indeed forgot to enter the memo field?", 669 "I guess you forgot to key in memo field when you sent ? ", 670 "this friend of mine also tried to send her XLM to the same exchange and she found the same problem.....", 671 "Exchange usually requires you to enter memo field whereas personal wallet you don't have to. ", 672 "yes this is the one", 673 "yes", 674 "Lumen = XLM and yes it's 918 xlm and I can see that they were sent from my wallet in Keybase but they never arrived in my wallet at the exchange. Does that complicate things that the receiving wallet is one of an exchange?", 675 "@inep ", 676 "Hmmmm.... what is the lumen? I am not sure that I know what that is...", 677 "Is that 918xlm that you recently transferred. Looks like it went through .. ? Was it your another wallet or exchange ?", 678 "I am nieuw to Keybase and Crypto and am verry excited. I was introdeuced to Keybase via a friend who gave me the possibility of joining in time for the Airdrop. Being new to Keybase I wanted to transfer my Lumen to another wallet. The address is correct but I have not received the Lumen in the wallet that I sent it to. Can anyone explain to me what I did wrong and if I can correct it?", 679 "Hello Everyone,", 680 "Very nice, thank you @chindraba ", 681 ":tada:", 682 "For \"denyability\" you can create a shadow account, encrypt to that account, removing yourself, and save the file. Anyone with access to the creating account (including rubber-hose) will not be able to decrypt it. As long as they don't know the other account is you, it remains private.", 683 "Being able to remove \"self\" from the encryption list, once there is someone else listed, is a nice trick too.", 684 "That does not happen, however for the encrypt function. So if the same file is to be encrypted to multiple recipients individually, it has to be renamed manually between encryptions.", 685 "Amazing and very useful for me. I have many folders and files and this greatly simplifies my workflow", 686 "The decrypt/verify functions will not overwrite existing files. Adding a (1), (2),... to the file name (not the extension if any).", 687 "It seems to be well behaved, in Linux at least.", 688 "Yes, just tried it myself on Linux. Works the same as you report on Mac. Decryption works just as easily too.", 689 "If I drag the file, I get binary which would be unsafe to paste in a message anywhere.", 690 "If I enter the text directly I get text I can paste in a message somewhere.", 691 "One problem is that it does not allow the option to have it ASCII armored, or what ever the term is relative to saltpack.", 692 "its the same", 693 "shut up", 694 "lol", 695 "never used a file manager", 696 "don't know how to drag and drop a file in linux", 697 "I tried the new update on a Mac. The crypto tab has an interesting behavior: If you drag and drop a file to sign or encrypt, the output is generated and stored in the same location as the original file. This works on both local and remote (Google Drive File Stream) folders. I am amazed! Does this work the same also on Windows and Linux?", 698 ":wave:", 699 "Hello ", 700 "Where ", 701 ":wave:", 702 "Submit GitHub issue. Haa.. ", 703 "True but... It can't be used for it's purpose anymore.", 704 "i mean you can leave the channel lol", 705 "So will it ever be deleted since it's basically useless now?", 706 "Ah ok. Thought there was some issue after the update", 707 "people would just flip constantly", 708 "but as keybasefriends grew it ended up causing issues", 709 "it was", 710 "Huh, thought that's the purpose of the channel", 711 "it ends up bogging down every single client connected and the server for minutes", 712 "(i know it sounds silly)", 713 "flip was disabled in #flip ", 714 "You don't have to wait. Tell others about Keybase and about the security threats built into Whatsapp, and get everyone to use Keybase instead. No waiting and better security. What could be better?", 715 "Quick question for any engineering managers here... for React Native (iOS, Android, Desktop + Web App) products, do you guys recommend typescript? Seems like extra overhead", 716 "Hey everyone nice to meet you all of you guys", 717 ":wave:", 718 "i mean they have @keybase but that's only for keybase employees lol :) ", 719 "I hope it would be on phones soon. Can't wait to send encrypted messages on Whatsapp :grin:", 720 "this is that place :) thanks for the kind words", 721 "does the dev team at keybase have a team or channel (I assume they don't want to be TOO public). Bravo to them on the past few updates.", 722 "Thanks", 723 "that's what I thought", 724 "a name is either a user or a team", 725 "ok", 726 "they can't overlap", 727 "yes", 728 "Do team names and user names share a namespace? In other words can a user have the same name as a team?", 729 "༼ つ ◕‿◕ ༽つ ", 730 "Keybase seems to only render the lower half of some of the weirder characters ", 731 "ATM desktop only", 732 "How does one install it?", 733 "In development (hopefully)", 734 "https://www.fsf.org/facebook", 735 "Where is crypt on my android app?", 736 } 737 b.ResetTimer() 738 for i := 0; i < b.N; i++ { 739 for _, msg := range messages { 740 DecorateWithLinks(context.TODO(), msg) 741 } 742 } 743 } 744 745 type decorateLinkTest struct { 746 body string 747 result string 748 } 749 750 func TestDecorateLinks(t *testing.T) { 751 cases := []decorateLinkTest{ 752 { 753 body: "click www.google.com", 754 result: "click $>kb$eyJ0eXAiOjQsImxpbmsiOnsidXJsIjoid3d3Lmdvb2dsZS5jb20iLCJwdW55Y29kZSI6IiJ9fQ==$<kb$", 755 }, 756 { 757 body: "https://maps.google.com?q=Goddess%20and%20the%20Baker,%20Legacy%20Tower,%20S%20Wabash%20Ave,%20Chicago,%20IL%2060603&ftid=0x880e2ca4623987cb:0x8b9a49f6050a873a&hl=en-US&gl=us", 758 result: "$>kb$eyJ0eXAiOjQsImxpbmsiOnsidXJsIjoiaHR0cHM6Ly9tYXBzLmdvb2dsZS5jb20/cT1Hb2RkZXNzJTIwYW5kJTIwdGhlJTIwQmFrZXIsJTIwTGVnYWN5JTIwVG93ZXIsJTIwUyUyMFdhYmFzaCUyMEF2ZSwlMjBDaGljYWdvLCUyMElMJTIwNjA2MDNcdTAwMjZmdGlkPTB4ODgwZTJjYTQ2MjM5ODdjYjoweDhiOWE0OWY2MDUwYTg3M2FcdTAwMjZobD1lbi1VU1x1MDAyNmdsPXVzIiwicHVueWNvZGUiOiIifX0=$<kb$", 759 }, 760 { 761 body: "10.0.0.24", 762 result: "$>kb$eyJ0eXAiOjQsImxpbmsiOnsidXJsIjoiMTAuMC4wLjI0IiwicHVueWNvZGUiOiIifX0=$<kb$", 763 }, 764 { 765 body: "ws-0.localdomain", 766 result: "ws-0.localdomain", 767 }, 768 { 769 body: "https://companyname.sharepoint.com/:f:/s/site-collection-name/subsite-name/Ds10TaJKAKhMp1hE0B_42WcBVhTHD3EQJKWhGprKFP3vpQ?e=14ohmf", 770 result: "$>kb$eyJ0eXAiOjQsImxpbmsiOnsidXJsIjoiaHR0cHM6Ly9jb21wYW55bmFtZS5zaGFyZXBvaW50LmNvbS86Zjovcy9zaXRlLWNvbGxlY3Rpb24tbmFtZS9zdWJzaXRlLW5hbWUvRHMxMFRhSktBS2hNcDFoRTBCXzQyV2NCVmhUSEQzRVFKS1doR3ByS0ZQM3ZwUT9lPTE0b2htZiIsInB1bnljb2RlIjoiIn19$<kb$", 771 }, 772 { 773 body: "http://keybase.io/mikem;", 774 result: "$>kb$eyJ0eXAiOjQsImxpbmsiOnsidXJsIjoiaHR0cDovL2tleWJhc2UuaW8vbWlrZW0iLCJwdW55Y29kZSI6IiJ9fQ==$<kb$;", 775 }, 776 { 777 body: "keybase.io, hi", 778 result: "$>kb$eyJ0eXAiOjQsImxpbmsiOnsidXJsIjoia2V5YmFzZS5pbyIsInB1bnljb2RlIjoiIn19$<kb$, hi", 779 }, 780 { 781 body: "https://en.wikipedia.org/wiki/J/Z_(New_York_City_Subway_service)", 782 result: "$>kb$eyJ0eXAiOjQsImxpbmsiOnsidXJsIjoiaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvSi9aXyhOZXdfWW9ya19DaXR5X1N1YndheV9zZXJ2aWNlKSIsInB1bnljb2RlIjoiIn19$<kb$", 783 }, 784 { 785 body: "(keybase.io)", 786 result: "($>kb$eyJ0eXAiOjQsImxpbmsiOnsidXJsIjoia2V5YmFzZS5pbyIsInB1bnljb2RlIjoiIn19$<kb$)", 787 }, 788 { 789 body: "https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/unicode-range", 790 result: "$>kb$eyJ0eXAiOjQsImxpbmsiOnsidXJsIjoiaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvQ1NTL0Bmb250LWZhY2UvdW5pY29kZS1yYW5nZSIsInB1bnljb2RlIjoiIn19$<kb$", 791 }, 792 { 793 body: "\u202ehttps://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/unicode-range", 794 result: "\u202ehttps://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/unicode-range", 795 }, 796 { 797 body: "\u202e\u202dhttps://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/unicode-range", 798 result: "\u202e\u202d$>kb$eyJ0eXAiOjQsImxpbmsiOnsidXJsIjoiaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvQ1NTL0Bmb250LWZhY2UvdW5pY29kZS1yYW5nZSIsInB1bnljb2RlIjoiIn19$<kb$", 799 }, 800 { 801 body: "`www.google.com`", 802 result: "`www.google.com`", 803 }, 804 { 805 body: "```www.google.com```", 806 result: "```www.google.com```", 807 }, 808 { 809 body: "> www.google.com", 810 result: "> $>kb$eyJ0eXAiOjQsImxpbmsiOnsidXJsIjoid3d3Lmdvb2dsZS5jb20iLCJwdW55Y29kZSI6IiJ9fQ==$<kb$", 811 }, 812 { 813 body: "nytimes.json", 814 result: "nytimes.json", 815 }, 816 { 817 body: "mike.maxim@gmail.com", 818 result: "$>kb$eyJ0eXAiOjUsIm1haWx0byI6eyJ1cmwiOiJtaWtlLm1heGltQGdtYWlsLmNvbSIsInB1bnljb2RlIjoiIn19$<kb$", 819 }, 820 { 821 body: "mailto:mike.maxim@gmail.com", 822 result: "mailto:$>kb$eyJ0eXAiOjUsIm1haWx0byI6eyJ1cmwiOiJtaWtlLm1heGltQGdtYWlsLmNvbSIsInB1bnljb2RlIjoiIn19$<kb$", 823 }, 824 { 825 body: "mike.maxim@gmail.com/google.com", 826 result: "$>kb$eyJ0eXAiOjUsIm1haWx0byI6eyJ1cmwiOiJtaWtlLm1heGltQGdtYWlsLmNvbSIsInB1bnljb2RlIjoiIn19$<kb$/google.com", 827 }, 828 { 829 body: "https://medium.com/@wouterarkink/https-medium-com-wouterarkink-how-to-send-money-to-anyone-in-the-world-by-only-knowing-their-social-handle-3180e6cd4e58", 830 result: "$>kb$eyJ0eXAiOjQsImxpbmsiOnsidXJsIjoiaHR0cHM6Ly9tZWRpdW0uY29tL0B3b3V0ZXJhcmtpbmsvaHR0cHMtbWVkaXVtLWNvbS13b3V0ZXJhcmtpbmstaG93LXRvLXNlbmQtbW9uZXktdG8tYW55b25lLWluLXRoZS13b3JsZC1ieS1vbmx5LWtub3dpbmctdGhlaXItc29jaWFsLWhhbmRsZS0zMTgwZTZjZDRlNTgiLCJwdW55Y29kZSI6IiJ9fQ==$<kb$", 831 }, 832 { 833 body: "https://drive.google.com/open?id=1BKcMML-uqOFAK-D4btEBlcoyodfvE4gg&authuser=cecile@keyba.se&usp=drive_fs", 834 result: "$>kb$eyJ0eXAiOjQsImxpbmsiOnsidXJsIjoiaHR0cHM6Ly9kcml2ZS5nb29nbGUuY29tL29wZW4/aWQ9MUJLY01NTC11cU9GQUstRDRidEVCbGNveW9kZnZFNGdnXHUwMDI2YXV0aHVzZXI9Y2VjaWxlQGtleWJhLnNlXHUwMDI2dXNwPWRyaXZlX2ZzIiwicHVueWNvZGUiOiIifX0=$<kb$", 835 }, 836 { 837 body: "@google.com", 838 result: "@google.com", 839 }, 840 { 841 body: "/keybase/team/keybase.staff_v8/candidates/feedback-template.md", 842 result: "/keybase/team/keybase.staff_v8/candidates/feedback-template.md", 843 }, 844 { 845 body: "#google.com", 846 result: "#$>kb$eyJ0eXAiOjQsImxpbmsiOnsidXJsIjoiZ29vZ2xlLmNvbSIsInB1bnljb2RlIjoiIn19$<kb$", 847 }, 848 { 849 body: "client/go/profiling/aggregate_timers.py", 850 result: "client/go/profiling/aggregate_timers.py", 851 }, 852 { 853 body: "cnn.com/@mike/index.html", 854 result: "$>kb$eyJ0eXAiOjQsImxpbmsiOnsidXJsIjoiY25uLmNvbS9AbWlrZS9pbmRleC5odG1sIiwicHVueWNvZGUiOiIifX0=$<kb$", 855 }, 856 { 857 body: "google.com/mike?email=mike@gmail.com", 858 result: "$>kb$eyJ0eXAiOjQsImxpbmsiOnsidXJsIjoiZ29vZ2xlLmNvbS9taWtlP2VtYWlsPW1pa2VAZ21haWwuY29tIiwicHVueWNvZGUiOiIifX0=$<kb$", 859 }, 860 { 861 body: "@keybase.bots.build.macos", 862 result: "@keybase.bots.build.macos", 863 }, 864 { 865 body: "keybase://team-page/keybasefriends", 866 result: "$>kb$eyJ0eXAiOjQsImxpbmsiOnsidXJsIjoia2V5YmFzZTovL3RlYW0tcGFnZS9rZXliYXNlZnJpZW5kcyIsInB1bnljb2RlIjoiIn19$<kb$", 867 }, 868 { 869 body: "keybase://team-page/keybasefriends https://github.com", 870 result: "$>kb$eyJ0eXAiOjQsImxpbmsiOnsidXJsIjoia2V5YmFzZTovL3RlYW0tcGFnZS9rZXliYXNlZnJpZW5kcyIsInB1bnljb2RlIjoiIn19$<kb$ $>kb$eyJ0eXAiOjQsImxpbmsiOnsidXJsIjoiaHR0cHM6Ly9naXRodWIuY29tIiwicHVueWNvZGUiOiIifX0=$<kb$", 871 }, 872 } 873 for _, c := range cases { 874 res := DecorateWithLinks(context.TODO(), c.body) 875 require.Equal(t, c.result, res, "incorrect encoding for body %s", c.body) 876 } 877 } 878 879 type configUsernamer struct { 880 libkb.ConfigReader 881 username libkb.NormalizedUsername 882 } 883 884 func (c configUsernamer) GetUsername() libkb.NormalizedUsername { 885 return c.username 886 } 887 888 func TestAddUserToTlfName(t *testing.T) { 889 tc := externalstest.SetupTest(t, "chat-utils", 0) 890 defer tc.Cleanup() 891 892 g := globals.NewContext(tc.G, &globals.ChatContext{}) 893 g.Env.SetConfig( 894 &configUsernamer{g.Env.GetConfig(), "charlie"}, g.Env.GetConfigWriter()) 895 896 priv := keybase1.TLFVisibility_PRIVATE 897 mem := chat1.ConversationMembersType_IMPTEAMNATIVE 898 s := AddUserToTLFName(g, "alice,bob", priv, mem) 899 require.Equal(t, "alice,bob,charlie", s) 900 s = AddUserToTLFName(g, "charlie", priv, mem) 901 require.Equal(t, "charlie,charlie", s) 902 s = AddUserToTLFName( 903 g, "alice,bob (conflicted copy 2019-02-14 #1)", priv, mem) 904 require.Equal(t, "alice,bob,charlie (conflicted copy 2019-02-14 #1)", s) 905 s = AddUserToTLFName( 906 g, "alice#bob", priv, mem) 907 require.Equal(t, "alice,charlie#bob", s) 908 s = AddUserToTLFName( 909 g, "alice#bob (conflicted copy 2019-02-14 #1)", priv, mem) 910 require.Equal(t, "alice,charlie#bob (conflicted copy 2019-02-14 #1)", s) 911 912 pub := keybase1.TLFVisibility_PUBLIC 913 s = AddUserToTLFName(g, "alice,bob", pub, mem) 914 require.Equal(t, "alice,bob", s) 915 } 916 917 func TestPresentConversationParticipantsLocal(t *testing.T) { 918 tofurkeyhq := "Tofurkey HQ" 919 tofurus := "Tofu-R-Us" 920 danny := "Danny" 921 rawParticipants := []chat1.ConversationLocalParticipant{ 922 { 923 Username: "[tofurkey@example.com]@email", 924 ContactName: &tofurkeyhq, 925 }, 926 { 927 Username: "18005558638@phone", 928 ContactName: &tofurus, 929 }, 930 { 931 Username: "ayoubd", 932 Fullname: &danny, 933 }, 934 { 935 Username: "example@twitter", 936 }, 937 } 938 res := PresentConversationParticipantsLocal(context.TODO(), rawParticipants) 939 940 require.Equal(t, res[0].ContactName, &tofurkeyhq) 941 require.Equal(t, res[0].Type, chat1.UIParticipantType_EMAIL) 942 943 require.Equal(t, res[1].ContactName, &tofurus) 944 require.Equal(t, res[1].Type, chat1.UIParticipantType_PHONENO) 945 946 require.Equal(t, res[2].Assertion, "ayoubd") 947 require.Equal(t, res[2].FullName, &danny) 948 require.Equal(t, res[2].Type, chat1.UIParticipantType_USER) 949 950 require.Equal(t, res[3].Assertion, "example@twitter") 951 require.Equal(t, res[3].Type, chat1.UIParticipantType_USER) 952 } 953 954 type contactStoreMock struct { 955 assertionToName map[string]string 956 } 957 958 func (c *contactStoreMock) SaveProcessedContacts(libkb.MetaContext, []keybase1.ProcessedContact) error { 959 return errors.New("contactStoreMock not impl") 960 } 961 962 func (c *contactStoreMock) RetrieveContacts(libkb.MetaContext) ([]keybase1.ProcessedContact, error) { 963 return nil, errors.New("contactStoreMock not impl") 964 } 965 966 func (c *contactStoreMock) RetrieveAssertionToName(libkb.MetaContext) (map[string]string, error) { 967 return c.assertionToName, nil 968 } 969 970 func (c *contactStoreMock) UnresolveContactsWithComponent(mctx libkb.MetaContext, 971 phoneNumber *keybase1.PhoneNumber, email *keybase1.EmailAddress) { 972 panic("unexpected call to UnresolveContactsWithComponent in mock") 973 } 974 975 func TestAttachContactNames(t *testing.T) { 976 tc := externalstest.SetupTest(t, "chat-utils", 0) 977 defer tc.Cleanup() 978 979 assertionToName := map[string]string{ 980 "[tofurkey@example.com]@email": "Tofu R-Key", 981 "18005558638@phone": "Alice", 982 } 983 984 mock := &contactStoreMock{assertionToName} 985 tc.G.SyncedContactList = mock 986 987 rawParticipants := []chat1.ConversationLocalParticipant{ 988 { 989 Username: "[tofurkey@example.com]@email", 990 }, 991 { 992 Username: "18005558638@phone", 993 }, 994 { 995 Username: "ayoubd", 996 }, 997 { 998 Username: "example@twitter", 999 }, 1000 } 1001 1002 AttachContactNames(tc.MetaContext(), rawParticipants) 1003 require.NotNil(t, rawParticipants[0].ContactName) 1004 require.Equal(t, "Tofu R-Key", *rawParticipants[0].ContactName) 1005 require.NotNil(t, rawParticipants[1].ContactName) 1006 require.Equal(t, "Alice", *rawParticipants[1].ContactName) 1007 require.Nil(t, rawParticipants[2].ContactName) 1008 require.Nil(t, rawParticipants[3].ContactName) 1009 } 1010 1011 func TestTLFIsTeamID(t *testing.T) { 1012 teamID := keybase1.MakeTestTeamID(3, false) 1013 tlfID := chat1.TLFID(teamID.ToBytes()) 1014 require.True(t, tlfID.IsTeamID()) 1015 1016 tlfID = chat1.TLFID{0} 1017 require.False(t, tlfID.IsTeamID()) 1018 1019 uid := keybase1.MakeTestUID(3) 1020 tlfID = chat1.TLFID(uid.ToBytes()) 1021 require.False(t, tlfID.IsTeamID()) 1022 } 1023 1024 func TestSearchableRemoteConversationName(t *testing.T) { 1025 require.Equal(t, "zoommikem", searchableRemoteConversationNameFromStr("mikem,zoommikem", "mikem")) 1026 require.Equal(t, "zoommikem", searchableRemoteConversationNameFromStr("zoommikem,mikem", "mikem")) 1027 require.Equal(t, "zoommikem,max", 1028 searchableRemoteConversationNameFromStr("zoommikem,mikem,max", "mikem")) 1029 require.Equal(t, "zoommikem,zoomua", 1030 searchableRemoteConversationNameFromStr("zoommikem,mikem,zoomua", "mikem")) 1031 require.Equal(t, "joshblum,zoommikem,zoomua", 1032 searchableRemoteConversationNameFromStr("joshblum,zoommikem,mikem,zoomua", "mikem")) 1033 require.Equal(t, "joshblum,zoommikem,zoomua", 1034 searchableRemoteConversationNameFromStr("joshblum,zoommikem,mikem,zoomua,mikem", "mikem")) 1035 }