├── assets
└── img
│ ├── chorme.png
│ ├── icon128.png
│ ├── icon16.png
│ ├── icon19.png
│ ├── icon32.png
│ ├── icon38.png
│ ├── icon48.png
│ └── printDemonstracao.png
├── README.md
├── manifest.json
├── LICENSE
├── PoliticaPrivacidade.md
├── TermosDeCondicao.md
└── src
└── main.js
/assets/img/chorme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubensflinco/nubank-ofx/HEAD/assets/img/chorme.png
--------------------------------------------------------------------------------
/assets/img/icon128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubensflinco/nubank-ofx/HEAD/assets/img/icon128.png
--------------------------------------------------------------------------------
/assets/img/icon16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubensflinco/nubank-ofx/HEAD/assets/img/icon16.png
--------------------------------------------------------------------------------
/assets/img/icon19.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubensflinco/nubank-ofx/HEAD/assets/img/icon19.png
--------------------------------------------------------------------------------
/assets/img/icon32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubensflinco/nubank-ofx/HEAD/assets/img/icon32.png
--------------------------------------------------------------------------------
/assets/img/icon38.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubensflinco/nubank-ofx/HEAD/assets/img/icon38.png
--------------------------------------------------------------------------------
/assets/img/icon48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubensflinco/nubank-ofx/HEAD/assets/img/icon48.png
--------------------------------------------------------------------------------
/assets/img/printDemonstracao.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubensflinco/nubank-ofx/HEAD/assets/img/printDemonstracao.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NuBank OFX - Meu Dinheiro Web
2 | Adiciona um botão para exportação do extrato do cartão de credito do Nubank para OFX otimizado para Meu Dinheiro Web e outras plataformas financeira que aceite arquivos .OFX, com opção de exportar com a data das transalações na fatura ou com as datas da compra da transalação que geralmente tem uma diferença de 1 dia.
3 |
4 | 
5 |
6 |
7 | ## Instale a extensão no seu navegador:
8 | [](https://chromewebstore.google.com/detail/ccaelbgnidpokbjopmckbbmggkokbfjc?hl=pt-BR)
9 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 3,
3 | "name": "NuBank OFX - Meu Dinheiro Web",
4 | "description": "Adicione botão para exportar extrato OFX do NuBank para Meu Dinheiro Web com data da fatura ou da compra.",
5 | "version": "4.0.2",
6 | "icons": {
7 | "16": "assets/img/icon16.png",
8 | "19": "assets/img/icon19.png",
9 | "32": "assets/img/icon32.png",
10 | "38": "assets/img/icon38.png",
11 | "48": "assets/img/icon48.png",
12 | "128": "assets/img/icon128.png"
13 | },
14 | "action": {
15 | "default_icon": {
16 | "16": "assets/img/icon16.png",
17 | "19": "assets/img/icon19.png",
18 | "32": "assets/img/icon32.png",
19 | "38": "assets/img/icon38.png",
20 | "48": "assets/img/icon48.png",
21 | "128": "assets/img/icon128.png"
22 | }
23 | },
24 | "content_scripts": [{
25 | "matches": ["https://*.nubank.com.br/*"],
26 | "js": ["src/main.js"]
27 | }]
28 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2023 Rubens Flinco
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/PoliticaPrivacidade.md:
--------------------------------------------------------------------------------
1 | Política Privacidade
2 | --------------------
3 |
4 | A sua privacidade é importante para nós. É política do Nubank OFX respeitar a sua privacidade em relação a qualquer informação sua que possamos coletar no site [Nubank OFX](https://github.com/rubensflinco/nubank-ofx), e outros sites que possuímos e operamos.
5 |
6 | Solicitamos informações pessoais apenas quando realmente precisamos delas para lhe fornecer um serviço. Fazemo-lo por meios justos e legais, com o seu conhecimento e consentimento. Também informamos por que estamos coletando e como será usado.
7 |
8 | Apenas retemos as informações coletadas pelo tempo necessário para fornecer o serviço solicitado. Quando armazenamos dados, protegemos dentro de meios comercialmente aceitáveis para evitar perdas e roubos, bem como acesso, divulgação, cópia, uso ou modificação não autorizados.
9 |
10 | Não compartilhamos informações de identificação pessoal publicamente ou com terceiros, exceto quando exigido por lei.
11 |
12 | O nosso site pode ter links para sites externos que não são operados por nós. Esteja ciente de que não temos controle sobre o conteúdo e práticas desses sites e não podemos aceitar responsabilidade por suas respectivas políticas de privacidade.
13 |
14 | Você é livre para recusar a nossa solicitação de informações pessoais, entendendo que talvez não possamos fornecer alguns dos serviços desejados.
15 |
16 | O uso continuado de nosso site será considerado como aceitação de nossas práticas em torno de privacidade e informações pessoais. Se você tiver alguma dúvida sobre como lidamos com dados do usuário e informações pessoais, entre em contacto connosco.
17 |
18 | ### Compromisso do Usuário
19 |
20 | O usuário se compromete a fazer uso adequado dos conteúdos e da informação que o Nubank OFX oferece no site e com caráter enunciativo, mas não limitativo:
21 |
22 | * A) Não se envolver em atividades que sejam ilegais ou contrárias à boa fé a à ordem pública;
23 | * B) Não difundir propaganda ou conteúdo de natureza racista, xenofóbica ou azar, qualquer tipo de pornografia ilegal, de apologia ao terrorismo ou contra os direitos humanos;
24 | * C) Não causar danos aos sistemas físicos (hardwares) e lógicos (softwares) do Nubank OFX, de seus fornecedores ou terceiros, para introduzir ou disseminar vírus informáticos ou quaisquer outros sistemas de hardware ou software que sejam capazes de causar danos anteriormente mencionados.
25 |
26 | ### Mais informações
27 |
28 | Esperemos que esteja esclarecido e, como mencionado anteriormente, se houver algo que você não tem certeza se precisa ou não, geralmente é mais seguro deixar os cookies ativados, caso interaja com um dos recursos que você usa em nosso site.
29 |
30 | Esta política é efetiva a partir de 24 July 2024 03:39
--------------------------------------------------------------------------------
/TermosDeCondicao.md:
--------------------------------------------------------------------------------
1 | 1\. Termos
2 | ----------
3 |
4 | Ao acessar ao site [Nubank OFX](https://github.com/rubensflinco/nubank-ofx), concorda em cumprir estes termos de serviço, todas as leis e regulamentos aplicáveis e concorda que é responsável pelo cumprimento de todas as leis locais aplicáveis. Se você não concordar com algum desses termos, está proibido de usar ou acessar este site. Os materiais contidos neste site são protegidos pelas leis de direitos autorais e marcas comerciais aplicáveis.
5 |
6 | 2\. Uso de Licença
7 | ------------------
8 |
9 | É concedida permissão para baixar temporariamente uma cópia dos materiais (informações ou software) no site Nubank OFX , apenas para visualização transitória pessoal e não comercial. Esta é a concessão de uma licença, não uma transferência de título e, sob esta licença, você não pode:
10 |
11 | 1. modificar ou copiar os materiais;
12 | 2. usar os materiais para qualquer finalidade comercial ou para exibição pública (comercial ou não comercial);
13 | 3. tentar descompilar ou fazer engenharia reversa de qualquer software contido no site Nubank OFX;
14 | 4. remover quaisquer direitos autorais ou outras notações de propriedade dos materiais; ou
15 | 5. transferir os materiais para outra pessoa ou 'espelhe' os materiais em qualquer outro servidor.
16 |
17 | Esta licença será automaticamente rescindida se você violar alguma dessas restrições e poderá ser rescindida por Nubank OFX a qualquer momento. Ao encerrar a visualização desses materiais ou após o término desta licença, você deve apagar todos os materiais baixados em sua posse, seja em formato eletrónico ou impresso.
18 |
19 | 3\. Isenção de responsabilidade
20 | -------------------------------
21 |
22 | 1. Os materiais no site da Nubank OFX são fornecidos 'como estão'. Nubank OFX não oferece garantias, expressas ou implícitas, e, por este meio, isenta e nega todas as outras garantias, incluindo, sem limitação, garantias implícitas ou condições de comercialização, adequação a um fim específico ou não violação de propriedade intelectual ou outra violação de direitos.
23 | 2. Além disso, o Nubank OFX não garante ou faz qualquer representação relativa à precisão, aos resultados prováveis ou à confiabilidade do uso dos materiais em seu site ou de outra forma relacionado a esses materiais ou em sites vinculados a este site.
24 |
25 | 4\. Limitações
26 | --------------
27 |
28 | Em nenhum caso o Nubank OFX ou seus fornecedores serão responsáveis por quaisquer danos (incluindo, sem limitação, danos por perda de dados ou lucro ou devido a interrupção dos negócios) decorrentes do uso ou da incapacidade de usar os materiais em Nubank OFX, mesmo que Nubank OFX ou um representante autorizado da Nubank OFX tenha sido notificado oralmente ou por escrito da possibilidade de tais danos. Como algumas jurisdições não permitem limitações em garantias implícitas, ou limitações de responsabilidade por danos conseqüentes ou incidentais, essas limitações podem não se aplicar a você.
29 |
30 | 5\. Precisão dos materiais
31 | --------------------------
32 |
33 | Os materiais exibidos no site da Nubank OFX podem incluir erros técnicos, tipográficos ou fotográficos. Nubank OFX não garante que qualquer material em seu site seja preciso, completo ou atual. Nubank OFX pode fazer alterações nos materiais contidos em seu site a qualquer momento, sem aviso prévio. No entanto, Nubank OFX não se compromete a atualizar os materiais.
34 |
35 | 6\. Links
36 | ---------
37 |
38 | O Nubank OFX não analisou todos os sites vinculados ao seu site e não é responsável pelo conteúdo de nenhum site vinculado. A inclusão de qualquer link não implica endosso por Nubank OFX do site. O uso de qualquer site vinculado é por conta e risco do usuário.
39 |
40 |
41 |
42 | ### Modificações
43 |
44 | O Nubank OFX pode revisar estes termos de serviço do site a qualquer momento, sem aviso prévio. Ao usar este site, você concorda em ficar vinculado à versão atual desses termos de serviço.
45 |
46 | ### Lei aplicável
47 |
48 | Estes termos e condições são regidos e interpretados de acordo com as leis do Nubank OFX e você se submete irrevogavelmente à jurisdição exclusiva dos tribunais naquele estado ou localidade.
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | const selectorCss = {
3 | extensionActiveInPage: '.extension-nubank-ofx-meu-dinheiro-web',
4 | titlePage: '[data-area="main-header"] .chakra-text',
5 | headerBtns: '[data-area="main-body"] div>div:nth-child(2)>div:last-child>div:last-child>div',
6 | openMonth: '[data-area="main-body"] > div > div > div > div > div > button:nth-child(3) > span',
7 | invoiceItem: '[data-area="main-body"] > div > div> table > tbody tr[tabindex="0"]',
8 | invoice: {
9 | description: 'td:nth-child(4) p',
10 | date: 'td:nth-child(1) p',
11 | amount: 'td:nth-child(5) p'
12 | }
13 | }
14 |
15 | const startOfx = () => {
16 | return `
17 | OFXHEADER:100
18 | DATA:OFXSGML
19 | VERSION:102
20 | SECURITY:NONE
21 | ENCODING:USASCII
22 | CHARSET:1252
23 | COMPRESSION:NONE
24 | OLDFILEUID:NONE
25 | NEWFILEUID:NONE
26 |
27 |
28 |
29 |
30 |
31 | `;
32 | }
33 |
34 | const endOfx = () =>
35 | `
36 |
37 |
38 |
39 |
40 | `;
41 |
42 | const bankStatement = (date, amount, description) =>
43 | `
44 |
45 | OTHER
46 | ${date}
47 | ${amount}
48 | ${description}
49 | `;
50 |
51 | const normalizeAmount = (text) => {
52 | let amount = (text.replace('R$', '').replace('.', '').replace(',', '.')).trim();
53 | if (String(amount).includes("-")) {
54 | amount = amount.replace('-', '+');
55 | } else {
56 | amount = "-" + amount;
57 | }
58 | return amount;
59 | }
60 |
61 | const normalizeDay = (date) =>
62 | date.split(' ')[0];
63 |
64 | const normalizeMonth = (date) => {
65 | const month = String(date.split(' ')[1]).toLocaleLowerCase()
66 | const months = {
67 | 'jan': '01',
68 | 'janeiro': '01',
69 | 'fev': '02',
70 | 'fevereiro': '02',
71 | 'mar': '03',
72 | 'março': '03',
73 | 'abr': '04',
74 | 'abril': '04',
75 | 'mai': '05',
76 | 'maio': '05',
77 | 'jun': '06',
78 | 'junho': '06',
79 | 'jul': '07',
80 | 'julho': '07',
81 | 'ago': '08',
82 | 'agosto': '08',
83 | 'set': '09',
84 | 'setembro': '09',
85 | 'out': '10',
86 | 'outubro': '10',
87 | 'nov': '11',
88 | 'novembro': '11',
89 | 'dez': '12',
90 | 'dezembro': '12'
91 | }
92 |
93 | return months[month];
94 | }
95 |
96 | const normalizeYear = (date, ifJan = true) => {
97 | const openMonth = (document.querySelector(selectorCss.openMonth).textContent.trim()).toLocaleLowerCase();
98 | const dateArray = String(date).split(' ');
99 | if (dateArray.length > 2) {
100 | return dateArray[2];
101 | } else {
102 | if ((ifJan) && (openMonth.includes("janeiro"))) {
103 | return Number(new Date().getFullYear()) - 1;
104 | } else {
105 | return new Date().getFullYear();
106 | }
107 | };
108 | }
109 |
110 | const normalizeDate = (date, dataCompra = false) => {
111 | date = preNormalizeDate(date);
112 | const [year, month, day] = [
113 | normalizeYear(date),
114 | normalizeMonth(date),
115 | normalizeDay(date)
116 | ];
117 | if (dataCompra === false) {
118 | const newDate = new Date(`${year}-${month}-${day}`);
119 | newDate.setDate(newDate.getDate() + 1);
120 |
121 | const normalizedDate = newDate.toISOString().slice(0, 10).replace(/-/g, '');
122 | return normalizedDate;
123 | } else {
124 | const dataFatura = `${year}${month}${day}`;
125 | return dataFatura;
126 | }
127 | };
128 |
129 | const preNormalizeDate = (date) => {
130 | date = String(date).toLocaleLowerCase();
131 | const today = new Date(); // Get the current date and time
132 | const yesterday = new Date(today);
133 | yesterday.setDate(today.getDate() - 1); // Subtract one day
134 |
135 | const options = {
136 | 'hoje': `${today.getDate()} ${today.toLocaleString('pt-BR', { month: 'long' })}`,
137 | 'ontem': `${yesterday.getDate()} ${yesterday.toLocaleString('pt-BR', { month: 'long' })}`,
138 | 'segunda': `${obterDataSemanaAtual('segunda')}`,
139 | 'terça': `${obterDataSemanaAtual('terça')}`,
140 | 'quarta': `${obterDataSemanaAtual('quarta')}`,
141 | 'quinta': `${obterDataSemanaAtual('quinta')}`,
142 | 'sexta': `${obterDataSemanaAtual('sexta')}`,
143 | 'sábado': `${obterDataSemanaAtual('sábado')}`,
144 | 'domingo': `${obterDataSemanaAtual('domingo')}`
145 | }
146 |
147 | const result = options[date];
148 | if(result){
149 | return result;
150 | }else{
151 | return date;
152 | }
153 | }
154 |
155 | function obterDataSemanaAtual(nomeDiaDaSemana = null) {
156 | nomeDiaDaSemana = String(nomeDiaDaSemana).toLocaleLowerCase();
157 | const diasSemana = {
158 | 'domingo': 0,
159 | 'segunda': 1,
160 | 'terça': 2,
161 | 'quarta': 3,
162 | 'quinta': 4,
163 | 'sexta': 5,
164 | 'sábado': 6
165 | };
166 |
167 | const hoje = new Date();
168 | const diaAtual = hoje.getDay(); // 0 (domingo) a 6 (sábado)
169 |
170 | const datasSemana = {};
171 |
172 | for (const [diaNome, diaIndice] of Object.entries(diasSemana)) {
173 | const diferencaDias = diaIndice - diaAtual;
174 | const data = new Date(hoje);
175 | data.setDate(hoje.getDate() + diferencaDias);
176 | datasSemana[diaNome] = `${data.getDate()} ${data.toLocaleString('pt-BR', { month: 'long' })}`;
177 | }
178 |
179 | if(nomeDiaDaSemana == null){
180 | return datasSemana;
181 | }else{
182 | return datasSemana[nomeDiaDaSemana];
183 | }
184 | }
185 |
186 | const exportOfx = (ofx, appendNameFile) => {
187 | const openMonth = " " + document.querySelector(selectorCss.openMonth).textContent.trim().toLocaleLowerCase();
188 | const period = normalizeYear(openMonth, false) + "-" + normalizeMonth(openMonth);
189 | link = document.createElement("a");
190 | link.setAttribute("href", 'data:application/x-ofx,' + encodeURIComponent(ofx));
191 | link.setAttribute("download", `nubank-${appendNameFile}-${period}-${openMonth}.ofx`);
192 | link.click();
193 | }
194 |
195 | const generateOfx = (dataCompra = false) => {
196 | let ofx = startOfx();
197 | let saveDate = "";
198 |
199 | const invoiceItems = document.querySelectorAll(selectorCss.invoiceItem);
200 | invoiceItems.forEach(function (charge) {
201 | if (charge.getAttribute('tabindex') == '0') {
202 | const description = charge.querySelector(selectorCss.invoice.description).textContent.trim();
203 | let dateCurent = charge.querySelector(selectorCss.invoice.date)?.textContent;
204 | if((dateCurent == "") || (dateCurent == null) || (dateCurent == undefined)){
205 | dateCurent = saveDate;
206 | }else{
207 | saveDate = dateCurent;
208 | }
209 | const date = normalizeDate(dateCurent, dataCompra);
210 | const amount = normalizeAmount(charge.querySelector(selectorCss.invoice.amount).textContent);
211 |
212 | ofx += bankStatement(date, amount, description);
213 | }
214 | });
215 |
216 | ofx += endOfx();
217 | if (dataCompra === true) {
218 | exportOfx(ofx, "data-compra");
219 | } else {
220 | exportOfx(ofx, "data-fatura");
221 | }
222 | }
223 |
224 | const createExportButtonNew = () => {
225 | const div = document.createElement('div');
226 | div.classList.add('extension-nubank-ofx-meu-dinheiro-web');
227 |
228 | const btn1 = document.createElement('button');
229 | btn1.classList.add('css-nde0hv');
230 | btn1.setAttribute('role', 'gen-ofx');
231 | btn1.textContent = "Exportar para OFX (Data da fatura)";
232 | btn1.addEventListener('click', () => { generateOfx(false) });
233 |
234 | const btn2 = document.createElement('button');
235 | btn2.classList.add('css-nde0hv');
236 | btn2.setAttribute('role', 'gen-ofx-2');
237 | btn2.textContent = "Exportar para OFX (Data da compra)";
238 | btn2.addEventListener('click', () => { generateOfx(true) });
239 |
240 | div.appendChild(btn1);
241 | div.appendChild(document.createElement('br'));
242 | div.appendChild(btn2);
243 |
244 | return div;
245 | }
246 |
247 | const insertExportButtonCallback = (mutationList, observer) => {
248 | const extensionActiveInPage = document.querySelectorAll(selectorCss.extensionActiveInPage);
249 | const titlePage = document.querySelectorAll(selectorCss.titlePage)?.[0]?.textContent;
250 | const headerBtns = document.querySelectorAll(selectorCss.headerBtns);
251 |
252 | if (mutationList === null || mutationList === undefined || (mutationList?.length <= 0)) {
253 | return;
254 | };
255 |
256 | if (extensionActiveInPage === null || extensionActiveInPage === undefined || (extensionActiveInPage?.length > 0)) {
257 | return;
258 | };
259 |
260 | if (titlePage === null || titlePage === undefined || (titlePage !== "Resumo de faturas")) {
261 | return;
262 | };
263 |
264 | if (headerBtns?.length > 0) {
265 | for (let i = 0; i < headerBtns.length; i++) {
266 | const exportOfxButton = createExportButtonNew();
267 | headerBtns[i].parentNode.appendChild(exportOfxButton);
268 | }
269 | }
270 |
271 | }
272 |
273 | const observer = new MutationObserver(insertExportButtonCallback);
274 | observer.observe(document, { attributes: true, childList: true, subtree: true });
275 | })();
276 |
--------------------------------------------------------------------------------