Add facebook.com plugin #8
Loading…
Reference in a new issue
No description provided.
Delete branch "Arxcis:facebook"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Opening up PR here as well now, as I am stuck on how to parse the birthdays internationally.
The problem:
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
Fix date parsing internationallyFixed !8 (commit8b63b80a4d)Address issues raised by @nilsnh after his initial review.Fixed !8 (commitb3278d6271)Add fallbackFixed !8 (commitif birthday is undefined3e50b43896) and !8 (commitca08f7365f)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 Arxcis/rolodex#4Convert blacklist -> ignorelist.Fixed !8 (commit7bcb462926)Just allowFixed !8 (commitundefined.vcfto happen iffullNameis not found.4a2ad815e4)Fix bug where Birthday is not found ifFixed !8 (commitFacebook-languageis different thannavigator.language.52193a0e3e)WIP Add facebook.jsto WIP Add facebook.com pluginGreat 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.
Here's some feedback. :) Nothing dealbreaking here.
@ -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]I suggest moving this variable inside the
isProfilePagefunction. 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 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.
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
urlPatternsit 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
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 😆@Arxcis wrote in #8 (comment):
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. ☺️
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:
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. 🙌
@ -0,0 +23,6 @@export function extractData({ test = {} } = {}) {let fullNamelet photoUrllet yearlet monthlet dayMaybe combine year, month, day into
This makes it a little clearer what this date info is for.
@ -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}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.
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 👍
I think I may have cracked the conundrum of parsing birthdays. ✌️ @Arxcis
@nilsnh wrote in #8 (comment):
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 🚀
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. :)
@nilsnh wrote in #8 (comment):
I have some html data from Facebook indeed. I also stripped them for any
<script>, <style>, <link>, <iframe> <noscript>tags, massively reducing their size: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?
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.
./facebook-mobile.clean.html- full name found 🍏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) 💯
WIP Add facebook.com pluginto Add facebook.com plugin@Arxcis wrote in #8 (comment):
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. 🤷:)
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.`)Should we log warnings instead of errors? Any missing factoid from this extract logic will block the user from downloading anything else.
So if
fullNamedoes not exist, the user will get to downloadundefined.vcf?I guess that is ok? 🤷 👌
Logging warnings now, instead of returning (see: !8 (commit
4a2ad815e4))@ -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, ' ')I wonder if fullname is also given from
document.titleon mobile? Might be less brittle to target.Resolving this for now, as I am not failing hard if fullName is not found anymore, only console.warn("Fullname not found") 👍
@ -0,0 +147,4 @@function extractDesktop() {let fullName{const blacklist = [We should use terms such as blocklist or denylist. Ref: https://www.aswf.io/inclusive-language-guide/
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..
Fixed in !8 (commit
7bcb462926)@ -81,1 +97,4 @@fullName: 'Ola Nordmann',photoUrl:'facebook-mobile_files/443716248_10159874507140849_2048214128358563365_n_CvXD.jpg',birthday: undefined,I am pretty sure there should have been a birthday here 🤔 I will check...
Yes indeed I found a birthday, so this test is expected to fail.
Birthday expected:
Queryselector:
div.displayed:nth-child(5) > div:nth-child(1) div[role='button'], should find this snippet infacebook-mobile.clean.html:Fixed the test so that it correctly fails in !8 (commit
2b7cb6b9b5). Now onwards to make the test pass again 👍Now fixed the code so the test pass again 🟢 !8 (commit
49cfd794c5)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:

@Arxcis wrote in #8 (comment):
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)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
nnin my facebook profile but my firefox-lang isEnglish US. So facebook-preference overrides system-language (navigator):Same on mobile
And the result is that this now works correctly 👍
Well played and thank you @nilsnh ! 😄
Extra thoughts: And I think it is correct to use the
naviagor.languagewhen displaying the date in the popup later, and not use whatever random language is set in whatever currently page is viewed 👍Manually merged in
076c4f8b30Thank 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.
Pull request closed