Add facebook.com plugin #8

Closed
Arxcis wants to merge 0 commits from Arxcis:facebook into main
Collaborator

Opening up PR here as well now, as I am stuck on how to parse the birthdays internationally.

The problem:

2. january 2001    // 'no'
2 January 2001    // 'en-uk'
January 2 2001   // 'en-us'
...and so on

There is no native way to parse this. Third-party library may be required. Thus I am raising this issue up as an extension-wide issue, not just a facebook-plugin issue.

See Original PR for context.

TODO

Opening up PR here as well now, as I am stuck on how to parse the birthdays internationally. **The problem:** ``` 2. january 2001 // 'no' 2 January 2001 // 'en-uk' January 2 2001 // 'en-us' ...and so on ``` There is no native way to parse this. Third-party library may be required. Thus I am raising this issue up as an extension-wide issue, not just a facebook-plugin issue. See [Original PR](https://code.on.nilsnh.no/Arxcis/rolodex/pulls/1) for context. ## TODO - [x] ~Fix date parsing internationally~ Fixed https://code.on.nilsnh.no/nilsnh/rolodex/pulls/8/commits/8b63b80a4d6cec55afb43a395871936282bfd78d - [x] ~Address issues raised by @nilsnh after his initial review.~ Fixed https://code.on.nilsnh.no/nilsnh/rolodex/pulls/8/commits/b3278d62715e5cf679ac3b8a46b6b4202afd37a7 - [x] ~Add fallback `if birthday is undefined`~ Fixed https://code.on.nilsnh.no/nilsnh/rolodex/pulls/8/commits/3e50b43896d8ad9ae8c532f0810eab7945e14bd9 and https://code.on.nilsnh.no/nilsnh/rolodex/pulls/8/commits/ca08f7365f96253f300b153da567525bb072022a - [x] ~Export HTML of a desktop and a mobile version of facebook.com to enable testing of HTML structure. (see Linkedin tests for inspo)~ -> see separate PR https://code.on.nilsnh.no/Arxcis/rolodex/pulls/4 - [x] ~Convert blacklist -> ignorelist.~ Fixed https://code.on.nilsnh.no/nilsnh/rolodex/pulls/8/commits/7bcb462926d60819a564781773f9ca6d1351d0ab - [x] ~Just allow `undefined.vcf` to happen if `fullName` is not found.~ Fixed https://code.on.nilsnh.no/nilsnh/rolodex/pulls/8/commits/4a2ad815e40fb60363c4a0e3c44e4c195faa93b0 - [x] ~Fix bug where Birthday is not found if `Facebook-language` is different than `navigator.language`.~ Fixed https://code.on.nilsnh.no/nilsnh/rolodex/pulls/8/commits/52193a0e3e015ea564d5b44b35377c574126d6b1
Arxcis requested review from nilsnh 2026-07-25 22:35:17 +02:00
Arxcis changed title from WIP Add facebook.js to WIP Add facebook.com plugin 2026-07-26 00:38:22 +02:00
Owner

Great work @Arxcis !

Suggestion:

Can you export an html copy of a user facebook page for desktop and/or mobile?

I've done so for my LinkedIn user and tried to anonymize it in the process, changing names, IDs and using placeholder photos. You can check out how I've done the tests. It's not super thorough. Nevertheless it makes it possible for me to assist in the FB support. 😅 If you feel uncomfortable about this then skip it.

Maybe we can create a test case that targets date parsing specifically? Are dates shown using the time element? That might make it language agnostic.

Great work @Arxcis ! Suggestion: Can you export an html copy of a user facebook page for desktop and/or mobile? I've done so for my LinkedIn user and tried to anonymize it in the process, changing names, IDs and using placeholder photos. You can check out how I've done the tests. It's not super thorough. Nevertheless it makes it possible for me to assist in the FB support. 😅 If you feel uncomfortable about this then skip it. Maybe we can create a test case that targets date parsing specifically? Are dates shown using the [time element](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/time)? That might make it language agnostic.
nilsnh left a comment

Here's some feedback. :) Nothing dealbreaking here.

Here's some feedback. :) Nothing dealbreaking here.
Lines 9-12
@ -0,0 +5,7 @@
export const name = 'facebook.com'
const urlPatterns = [
/^https?:\/\/(www\.)?facebook.com\/(?!$|friends\b|reel\b|privacy\b|stories\b|settings\b|help\b)/, // desktop
/^https?:\/\/m\.facebook.com\/(?!$|friends\b|reel\b|privacy\b|stories\b|settings\b|help\b)/, // mobile
]
Owner

I suggest moving this variable inside the isProfilePage function. With function hoisting that lets us move functions such as this up and down this file without breaking anything.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions#function_hoisting

I suggest moving this variable inside the `isProfilePage` function. With function hoisting that lets us move functions such as this up and down this file without breaking anything. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions#function_hoisting
Author
Collaborator

I am not sure I understand @nilsnh. Moving the function above the declaration you are referring to will not break the code. The declaration of file-scoped variables may even be at the very end of the file for that matter. There is no ordering requirements when using file-scoped vars inside functions i javascript, as long as the variable is declared before the function is executed.

I am not sure I understand @nilsnh. Moving the function above the declaration you are referring to will not break the code. The declaration of file-scoped variables may even be at the very end of the file for that matter. There is no ordering requirements when using file-scoped vars inside functions i javascript, as long as the variable is declared before the function is executed.
Owner

Yeah, you're right. This code certainly works! I think I like to be more defensive in general. 😅 Though you'll probably find moments where I contradict myself as well.

When I see a file-scoped variable like urlPatterns it makes we wonder how many functions rely on it existing, and whether or not they would like to modify it. By inlining variables such as this we can be certain no other functions are relying on it or tampering with it.

I didn't know about variable hoisting, but I see that there are some cases where it might break if the function using a variable gets called early enough.

Example: https://jsbin.com/petopeqowo/1/edit?js,console

Yeah, you're right. This code certainly works! I think I like to be more defensive in general. 😅 Though you'll probably find moments where I contradict myself as well. When I see a file-scoped variable like `urlPatterns` it makes we wonder how many functions rely on it existing, and whether or not they would like to modify it. By inlining variables such as this we can be certain no other functions are relying on it or tampering with it. I didn't know about variable hoisting, but I see that there are some cases where it might break if the function using a variable gets called early enough. Example: https://jsbin.com/petopeqowo/1/edit?js,console
Author
Collaborator

I follow the same general principle of defensive programming, but...

Firstly:
Javascript has a super-cool feature for defensive programmes which I call file-scope-by default. All languages should have this, but not many do. I feel quite safe throwing variables out into file-scope. It is a safe-space for me. Knowing that no other files may access my file-scoped variables is enough for me. In javascript the file does what you need a class to do in many other languages (looking at you C,C++, C#, to name a few). The file-scope is the reason why I never reach for classes in javascript. Throwing a variable out into file scope for me it means declaring a private var foo = bar;.

Secondly
Having said that, I could go along with your proposal to inline variables, and it is probably fine, but there is something that hurts me a little bit, when you basically are creating a memory-leak on purpose, just to hide the variable in the function. Probably the garbage-collector will come and clean up after all the new Regex() on every function-call, but It just feel disrespectful towards the computer for me, on some tiny level, if you catch my drift 😆

I follow the same general principle of defensive programming, but... **Firstly:** Javascript has a super-cool feature for defensive programmes which I call file-scope-by default. All languages should have this, but not many do. I feel quite safe throwing variables out into file-scope. It is a safe-space for me. Knowing that no other files may access my file-scoped variables is enough for me. In javascript the file does what you need a class to do in many other languages (looking at you C,C++, C#, to name a few). The file-scope is the reason why I never reach for classes in javascript. Throwing a variable out into file scope for me it means declaring a `private var foo = bar;`. **Secondly** Having said that, I could go along with your proposal to inline variables, and it is probably fine, but there is something that hurts me a little bit, when you basically are creating a memory-leak on purpose, just to hide the variable in the function. Probably the garbage-collector will come and clean up after all the `new Regex()` on every function-call, but It just feel disrespectful towards the computer for me, on some tiny level, if you catch my drift 😆
Owner

@Arxcis wrote in #8 (comment):

Having said that, I could go along with your proposal to inline variables, and it is probably fine, but there is something that hurts me a little bit, when you basically are creating a memory-leak on purpose, just to hide the variable in the function.

Memory leaks are created when the GC can't be sure if a variable is not needed anymore. Function-scoped variable declarations are more likely to be GC'ed than file-level variable declarations. File-level scoped variables will probably never be de-allocated because they technically can be accessed by all functions. This is just my intuition. If you have some good articles on this I'd be happy to check them out. 💡

Unless the variable-defined-in-functions approach actually leaks memory or otherwise negatively affecting performance, I will try to function-scope variables because I would argue that leads to less opportunities for bugs to creep in because variables are declared close to where they are used and not accessible many other places.

I guess we could measure performance if/when we find time. 😅 If we learn that memory-pressure is not significant, I'd argue for an approach optimized for readability and maintainability. ☺️

@Arxcis wrote in https://code.on.nilsnh.no/nilsnh/rolodex/pulls/8#issuecomment-115: > Having said that, I could go along with your proposal to inline variables, and it is probably fine, but there is something that hurts me a little bit, when you basically are creating a memory-leak on purpose, just to hide the variable in the function. Memory leaks are created when the GC can't be sure if a variable is not needed anymore. Function-scoped variable declarations are more likely to be GC'ed than file-level variable declarations. File-level scoped variables will probably never be de-allocated because they technically can be accessed by all functions. This is just my intuition. If you have some good articles on this I'd be happy to check them out. 💡 Unless the variable-defined-in-functions approach actually leaks memory or otherwise negatively affecting performance, I will try to function-scope variables because I would argue that leads to less opportunities for bugs to creep in because variables are declared close to where they are used and not accessible many other places. I guess we could measure performance if/when we find time. 😅 If we learn that memory-pressure is not significant, I'd argue for an approach optimized for readability and maintainability. ☺️
Owner

Was trying to find some sources on this:

It's been a while I looked into this. But I forgot there's also the difference of stack vs. heap. Excerpt from the Node.js docs above:

In addition to the heap, V8 also uses the stack for memory management. The stack is a region of memory used to store local variables and function call information. Unlike the heap, which is managed by V8's garbage collector, the stack operates on a Last In, First Out (LIFO) principle.

Whenever a function is called, a new frame is pushed onto the stack. When the function returns, its frame is popped off. The stack is much smaller in size compared to the heap, but it is faster for memory allocation and deallocation. However, the stack has a limited size, and excessive use of memory (such as with deep recursion) can result in a stack overflow.

I'm all for writing efficient code but I'm unsure whether any of our approaches will meaningfully differ in any memory analysis at the scale we're writing. Also, a web extension's state seem to be quickly thrown out unless configured to be persistent. This doesn't mean I want to write sloppy code, but it means I think both our approaches are efficient enough for what we're building.

I think both approaches can co-exist. 🙌

Was trying to find some sources on this: - https://nodejs.org/learn/diagnostics/memory/understanding-and-tuning-memory - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Memory_management It's been a while I looked into this. But I forgot there's also the difference of stack vs. heap. Excerpt from the Node.js docs above: > > In addition to the heap, V8 also uses the stack for memory management. The stack is a region of memory used to store local variables and function call information. Unlike the heap, which is managed by V8's garbage collector, the stack operates on a Last In, First Out (LIFO) principle. > > Whenever a function is called, a new frame is pushed onto the stack. When the function returns, its frame is popped off. The stack is much smaller in size compared to the heap, but it is faster for memory allocation and deallocation. However, the stack has a limited size, and excessive use of memory (such as with deep recursion) can result in a stack overflow. I'm all for writing efficient code but I'm unsure whether any of our approaches will meaningfully differ in any memory analysis at the scale we're writing. Also, a web extension's state seem to be quickly thrown out [unless configured to be persistent](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Background_scripts). This doesn't mean I want to write sloppy code, but it means I think both our approaches are efficient enough for what we're building. I think both approaches can co-exist. 🙌
nilsnh marked this conversation as resolved
Lines 32-34
@ -0,0 +23,6 @@
export function extractData({ test = {} } = {}) {
let fullName
let photoUrl
let year
let month
let day
Owner

Maybe combine year, month, day into

let birthday = { year, month, day}

This makes it a little clearer what this date info is for.

Maybe combine year, month, day into ```javascript let birthday = { year, month, day} ``` This makes it a little clearer what this date info is for.
Arxcis marked this conversation as resolved
Lines 33-40
@ -0,0 +31,11 @@
return extractBirthday(test.birthday, test.locale)
}
if (/mobile|android|iphone|ipad|ipod/i.test(navigator.userAgent)) {
let err = extractMobile()
if (err) return err
} else {
let err = extractDesktop()
if (err) return err
}
Owner

Nice usage of user agent matching, I didn't know you can check for mobile this way.

On errors:

In general I think the code should tolerate not finding one or more factoids. For example if something happens and we cannot find a piece of information then the extension should still make it possible to download a vcard containing the remaining factoids.

This means that we should return/throw errors only if we in fact run into big failures.

Nice usage of user agent matching, I didn't know you can check for mobile this way. On errors: In general I think the code should tolerate not finding one or more factoids. For example if something happens and we cannot find a piece of information then the extension should still make it possible to download a vcard containing the remaining factoids. This means that we should return/throw errors only if we in fact run into big failures.
Author
Collaborator

On errors:
I agree. Not finding someting, should not block the user from downloading the rest, I think. I find that showing 'undefined' to the user, is a good enough error message, and makes it easy enough for the developer to locate where to start looking for potential problems 👍

On errors: I agree. Not finding someting, should not block the user from downloading the rest, I think. I find that showing 'undefined' to the user, is a good enough error message, and makes it easy enough for the developer to locate where to start looking for potential problems 👍
Arxcis marked this conversation as resolved
Owner

I think I may have cracked the conundrum of parsing birthdays. ✌️ @Arxcis

I think I may have cracked the conundrum of parsing birthdays. ✌️ @Arxcis
Author
Collaborator

@nilsnh wrote in #8 (comment):

I think I may have cracked the conundrum of parsing birthdays. ✌️ @Arxcis

Impressive and surprisingly compact solution to a huge problem. Well done and thank you 👍

I will do another commit soon, addressing your welcomed concerns and feedback 🚀

@nilsnh wrote in https://code.on.nilsnh.no/nilsnh/rolodex/pulls/8#issuecomment-90: > I think I may have cracked the conundrum of parsing birthdays. :v: @Arxcis Impressive and surprisingly compact solution to a huge problem. Well done and thank you 👍 I will do another commit soon, addressing your welcomed concerns and feedback 🚀
* Combine year,month,day into birthday.
* Fail more gracefully when getting errors parsing birthdays.
* Return values from inline-functions instead of writing directly to function-scoped variables.
* Hosting vcardify to the top, as it makes the reading of the plugin more pleasant.
* doc / added some more @notes on code decisions.
Owner

Is this ready to be reviewed @Arxcis ? If you find that it's a hazzle to get suitable html data from Facebook we can skip it. :)

Is this ready to be reviewed @Arxcis ? If you find that it's a hazzle to get suitable html data from Facebook we can skip it. :)
Author
Collaborator

@nilsnh wrote in #8 (comment):

Is this ready to be reviewed @Arxcis ? If you find that it's a hazzle to get suitable html data from Facebook we can skip it. :)

I have some html data from Facebook indeed. I also stripped them for any <script>, <style>, <link>, <iframe> <noscript> tags, massively reducing their size:

jonas@fedora:~/Nedlastingar$ du -h facebook-*
768K	facebook-birthday-only-day-month.clean.html
13M	    facebook-birthday-only-day-month.html
648K	facebook-birthday-with-only-year.clean.html
13M	    facebook-birthday-with-only-year.html
600K	facebook-full-birthday.clean.html
13M	    facebook-full-birthday.html
116K	facebook-mobile.clean.html
384K	facebook-mobile.html
628K	facebook-no-birthday.clean.html
13M	    facebook-no-birthday.html

The only hazzle for me is "how do I remove personal info"? Replace all full name with "Ola Nordmann" and "Kari Nordmann" and call it a day?

@nilsnh wrote in https://code.on.nilsnh.no/nilsnh/rolodex/pulls/8#issuecomment-105: > Is this ready to be reviewed @Arxcis ? If you find that it's a hazzle to get suitable html data from Facebook we can skip it. :) I have some html data from Facebook indeed. I also stripped them for any `<script>, <style>, <link>, <iframe> <noscript>` tags, massively reducing their size: ```sh jonas@fedora:~/Nedlastingar$ du -h facebook-* 768K facebook-birthday-only-day-month.clean.html 13M facebook-birthday-only-day-month.html 648K facebook-birthday-with-only-year.clean.html 13M facebook-birthday-with-only-year.html 600K facebook-full-birthday.clean.html 13M facebook-full-birthday.html 116K facebook-mobile.clean.html 384K facebook-mobile.html 628K facebook-no-birthday.clean.html 13M facebook-no-birthday.html ``` The only hazzle for me is "how do I remove personal info"? Replace all full name with "Ola Nordmann" and "Kari Nordmann" and call it a day?
Author
Collaborator

Update on testing

I moved the testing to a different branch, Arxcis/rolodex#4, as I have no idea on how to get the tests to pass, and I am stuck on it.

  • When I do manual click-test in browser - full name found 🍏
  • When I do manual css-select-test in inspector in browser - full name found 🍏
  • When I do manul css-select-test in vscode on ./facebook-mobile.clean.html - full name found 🍏
  • When I run automatic npm run test - Full name not found on this page 🔴 (see output here Arxcis/rolodex#4)

So some more work to do ...

Removing WIP-prefix on this PR, as it is considered ready for final review (if without tests ok) 💯

**Update on testing** I moved the testing to a different branch, https://code.on.nilsnh.no/Arxcis/rolodex/pulls/4, as I have no idea on how to get the tests to pass, and I am stuck on it. - When I do manual click-test in browser - full name found 🍏 - When I do manual css-select-test in inspector in browser - full name found 🍏 - When I do manul css-select-test in vscode on `./facebook-mobile.clean.html` - full name found 🍏 - When I run automatic `npm run test` - Full name not found on this page 🔴 (see output here https://code.on.nilsnh.no/Arxcis/rolodex/pulls/4) So some more work to do ... Removing WIP-prefix on this PR, as it is considered ready for final review (if without tests ok) 💯
Arxcis changed title from WIP Add facebook.com plugin to Add facebook.com plugin 2026-07-28 21:11:40 +02:00
Owner

@Arxcis wrote in #8 (comment):

The only hazzle for me is "how do I remove personal info"? Replace all full name with "Ola Nordmann" and "Kari Nordmann" and call it a day?

That's what I did. I also introduced some random numbers/letters in datapoints that looks like IDs. Though I'm not sure how sensitive certain URL/IDs are. 🤷:)

@Arxcis wrote in https://code.on.nilsnh.no/nilsnh/rolodex/pulls/8#issuecomment-106: > The only hazzle for me is "how do I remove personal info"? Replace all full name with "Ola Nordmann" and "Kari Nordmann" and call it a day? That's what I did. I also introduced some random numbers/letters in datapoints that looks like IDs. Though I'm not sure how sensitive certain URL/IDs are. 🤷:)
constructing a new Date selected today's date of 29.07.26
and trying to setMonth(1) resulted in march instead of february
because there's no february 29 unless during leap years.
JSDom doesn't support innerText and probably never will.

textContent is almost the same, but skips newlines.

See:
https://github.com/jsdom/jsdom/issues/1245
https://perfectionkills.com/the-poor-misunderstood-innerText/
changed from array-of-array to array-of-obj in test
because large array destructuring can lead to ordering
bugs.
Reviewed-on: Arxcis/rolodex#4
nilsnh self-assigned this 2026-07-29 13:35:20 +02:00
nilsnh left a comment

Thank you for your hard work! I think this is really close to go live. I have some comments, and I also made some changes so you can review mine as well @Arxcis. :)

Thank you for your hard work! I think this is really close to go live. I have some comments, and I also made some changes so you can review mine as well @Arxcis. :)
@ -0,0 +87,4 @@
`div[role='button'][aria-label] span[class='f2']`
)
if (!found) {
return new Error(`Full name not found on this mobile profile page.`)
Owner

Should we log warnings instead of errors? Any missing factoid from this extract logic will block the user from downloading anything else.

Should we log warnings instead of errors? Any missing factoid from this extract logic will block the user from downloading anything else.
Author
Collaborator

So if fullName does not exist, the user will get to download undefined.vcf?

  return downloadFile({
    filename: `${userProfile.fullName}.vcf`,
    data: plugin.vcardify(userProfile),
    tabId: tab.id,
  })

I guess that is ok? 🤷 👌

So if `fullName` does not exist, the user will get to download `undefined.vcf`? ```js return downloadFile({ filename: `${userProfile.fullName}.vcf`, data: plugin.vcardify(userProfile), tabId: tab.id, }) ``` I guess that is ok? 🤷 👌
Author
Collaborator

Logging warnings now, instead of returning (see: !8 (commit 4a2ad815e4))

Logging warnings now, instead of returning (see: https://code.on.nilsnh.no/nilsnh/rolodex/pulls/8/commits/4a2ad815e40fb60363c4a0e3c44e4c195faa93b0)
Arxcis marked this conversation as resolved
Lines 35-45
@ -0,0 +83,14 @@
function extractMobile() {
let fullName
{
let found = document.querySelector(
`div[role='button'][aria-label] span[class='f2']`
)
if (!found) {
return new Error(`Full name not found on this mobile profile page.`)
}
fullName = found.textContent
.trim()
.replaceAll('\n', ' ')
.replaceAll(/\s{2,}/g, ' ')
Owner

I wonder if fullname is also given from document.title on mobile? Might be less brittle to target.

I wonder if fullname is also given from `document.title` on mobile? Might be less brittle to target.
Author
Collaborator

Resolving this for now, as I am not failing hard if fullName is not found anymore, only console.warn("Fullname not found") 👍

Resolving this for now, as I am not failing hard if fullName is not found anymore, only console.warn("Fullname not found") 👍
Arxcis marked this conversation as resolved
@ -0,0 +147,4 @@
function extractDesktop() {
let fullName
{
const blacklist = [
Owner

We should use terms such as blocklist or denylist. Ref: https://www.aswf.io/inclusive-language-guide/

We should use terms such as blocklist or denylist. Ref: https://www.aswf.io/inclusive-language-guide/
Author
Collaborator

I agree 100%. Even putting inclusive language aside, using colors as the primary method of describing functionality I found is bad practice, almost always 👍 Just like green 🟢, yellow 🟡 and red status 🔴 -codes always needs to be explained further with helptexts anyway, to avoid misunderstandings outside of the traffic-world. And then I have not mentioned color-blindness yet..

I agree 100%. Even putting inclusive language aside, using colors as the primary method of describing functionality I found is bad practice, almost always 👍 Just like green 🟢, yellow 🟡 and red status 🔴 -codes always needs to be explained further with helptexts anyway, to avoid misunderstandings outside of the traffic-world. And then I have not mentioned color-blindness yet..
Author
Collaborator
Fixed in https://code.on.nilsnh.no/nilsnh/rolodex/pulls/8/commits/7bcb462926d60819a564781773f9ca6d1351d0ab
Arxcis marked this conversation as resolved
@ -81,1 +97,4 @@
fullName: 'Ola Nordmann',
photoUrl:
'facebook-mobile_files/443716248_10159874507140849_2048214128358563365_n_CvXD.jpg',
birthday: undefined,
Author
Collaborator

I am pretty sure there should have been a birthday here 🤔 I will check...

I am pretty sure there should have been a birthday here 🤔 I will check...
Author
Collaborator

Yes indeed I found a birthday, so this test is expected to fail.

Birthday expected:

birthday: {
  day: "04",
  month: "08",
  year: "1991"
}

Queryselector: div.displayed:nth-child(5) > div:nth-child(1) div[role='button'], should find this snippet in facebook-mobile.clean.html:

                  <div
                    role="button"
                    tabindex="0"
                    aria-label="Fødselsdato, 4. august 1991"
                    data-focusable="true"
                    data-tti-phase="-1"
                    data-action-id="32701"
                    data-actual-height="48"
                    data-mcomponent="MContainer"
                    data-type="container"
                    class="m bg-s1"
                  >
                    <div
                      aria-hidden="true"
                      data-tti-phase="-1"
                      data-mcomponent="ServerTextArea"
                      data-type="text"
                      class="m"
                    >
                      <div class="fl ac">
                        <div dir="auto" class="native-text rslh">
                          <span class="f3">󲂶</span>
                        </div>
                      </div>
                    </div>
                    <div
                      tabindex="0"
                      aria-label="4. august 1991"
                      data-focusable="true"
                      data-tti-phase="-1"
                      data-mcomponent="ServerTextArea"
                      data-type="text"
                      class="m"
                    >
                      <div dir="auto" class="native-text rslh">
                        <span class="f2">4. august 1991</span>
                      </div>
                    </div>
                  </div>
Yes indeed I found a birthday, so this test is expected to fail. Birthday expected: ```js birthday: { day: "04", month: "08", year: "1991" } ``` Queryselector: `div.displayed:nth-child(5) > div:nth-child(1) div[role='button']`, should find this snippet in `facebook-mobile.clean.html`: ```html <div role="button" tabindex="0" aria-label="Fødselsdato, 4. august 1991" data-focusable="true" data-tti-phase="-1" data-action-id="32701" data-actual-height="48" data-mcomponent="MContainer" data-type="container" class="m bg-s1" > <div aria-hidden="true" data-tti-phase="-1" data-mcomponent="ServerTextArea" data-type="text" class="m" > <div class="fl ac"> <div dir="auto" class="native-text rslh"> <span class="f3">󲂶</span> </div> </div> </div> <div tabindex="0" aria-label="4. august 1991" data-focusable="true" data-tti-phase="-1" data-mcomponent="ServerTextArea" data-type="text" class="m" > <div dir="auto" class="native-text rslh"> <span class="f2">4. august 1991</span> </div> </div> </div> ```
Author
Collaborator

Fixed the test so that it correctly fails in !8 (commit 2b7cb6b9b5). Now onwards to make the test pass again 👍

Fixed the test so that it correctly fails in https://code.on.nilsnh.no/nilsnh/rolodex/pulls/8/commits/2b7cb6b9b5a6cafa51c8d16b3eeb87c162035284. Now onwards to make the test pass again 👍
Author
Collaborator

Now fixed the code so the test pass again 🟢 !8 (commit 49cfd794c5)

Now fixed the code so the test pass again 🟢 https://code.on.nilsnh.no/nilsnh/rolodex/pulls/8/commits/49cfd794c590493348915f0d559111aced041742
Arxcis marked this conversation as resolved
* The query selector was not robust enough, so the failing test was doing gods work catching a bug.
* Updated the queryselector for catching birthdays on mobile to be more robust now so it does not rely on a fixed index. This will allow the birthday to move to a different index later without breaking the code.
* Hopefully....
Author
Collaborator

Issue / Birthday not found, because parser looks for english birthday but page shows norwegian

So have a bug where the birthday-parser is looking for english "february" but the page show norwegian "februar", which leads to the parser not finding the birthday.

raw:
image

## Issue / Birthday not found, because parser looks for english birthday but page shows norwegian So have a bug where the birthday-parser is looking for english `"february"` but the page show norwegian `"februar"`, which leads to the parser not finding the birthday. raw: ![image](/attachments/9aea5a75-7533-4650-a4bc-4f7f20ad26b4)
192 KiB
* Remove return new Error()
* Only logging errors now, failing gracefully.
Owner

@Arxcis wrote in #8 (comment):

So have a bug where the birthday-parser is looking for english "february" but the page show norwegian "februar", which leads to the parser not finding the birthday.

Good catch! 🎣 I tried a fix for it here, but I don't have a way to validate if this solves it. !8 (commit 52193a0e3e)

@Arxcis wrote in https://code.on.nilsnh.no/nilsnh/rolodex/pulls/8#issuecomment-146: > So have a bug where the birthday-parser is looking for english `"february"` but the page show norwegian `"februar"`, which leads to the parser not finding the birthday. Good catch! 🎣 I tried a fix for it here, but I don't have a way to validate if this solves it. https://code.on.nilsnh.no/nilsnh/rolodex/pulls/8/commits/52193a0e3e015ea564d5b44b35377c574126d6b1
Author
Collaborator

Good attempt @nilsnh

I will try and validate here and now.

Looks like on desktop this is the correct approach, as Facebook lists the correct lang there 👍 I set nn in my facebook profile but my firefox-lang is English US. So facebook-preference overrides system-language (navigator):

<html id="facebook" class="_9dls __fb-light-mode" lang="nn" dir="ltr">

Same on mobile

<html lang="nn" class="ssr fab rcs dpr-spacing unselectable">

And the result is that this now works correctly 👍

BEGIN:VCARD
VERSION:4.0
UID:https://www.facebook.com/herman.redactesen/
URL:https://www.facebook.com/herman.redactesen/
FN:Herman Redacted Redactesen
BDAY:1999-02-06

Well played and thank you @nilsnh ! 😄

Extra thoughts: And I think it is correct to use the naviagor.language when displaying the date in the popup later, and not use whatever random language is set in whatever currently page is viewed 👍

        const dFmt = new Intl.DateTimeFormat(navigator.locale, {
          month: 'long',
        })
Good attempt @nilsnh I will try and validate here and now. Looks like on desktop this is the correct approach, as Facebook lists the correct lang there 👍 I set `nn` in my facebook profile but my firefox-lang is `English US`. So facebook-preference overrides system-language (navigator): ```htm <html id="facebook" class="_9dls __fb-light-mode" lang="nn" dir="ltr"> ``` Same on mobile ```html <html lang="nn" class="ssr fab rcs dpr-spacing unselectable"> ``` And the result is that this now works correctly 👍 ``` BEGIN:VCARD VERSION:4.0 UID:https://www.facebook.com/herman.redactesen/ URL:https://www.facebook.com/herman.redactesen/ FN:Herman Redacted Redactesen BDAY:1999-02-06 ``` Well played and thank you @nilsnh ! 😄 Extra thoughts: And I think it is correct to use the `naviagor.language` when displaying the date in the popup later, and not use whatever random language is set in whatever currently page is viewed 👍 ```js const dFmt = new Intl.DateTimeFormat(navigator.locale, { month: 'long', }) ```
Owner

Manually merged in 076c4f8b30

Thank you again for your hard work @Arxcis! :) Also, I've enabled email notification support for this instance. Might make it easier to collaborate.

Aim to make a new release soonish.

Manually merged in https://code.on.nilsnh.no/nilsnh/rolodex/commit/076c4f8b3009cd1fc907c258d957b2dfb6925d38 Thank you again for your hard work @Arxcis! :) Also, I've enabled email notification support for this instance. Might make it easier to collaborate. Aim to make a new release soonish.
nilsnh closed this pull request 2026-07-30 21:26:58 +02:00
nilsnh deleted branch facebook 2026-07-30 21:28:18 +02:00

Pull request closed

Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
nilsnh/rolodex!8
No description provided.