├── .gitignore ├── icon-128.png ├── screenshot └── naver.png ├── loader.js ├── manifest.json ├── package.json ├── util.mjs ├── README.md ├── LICENSE ├── naver.mjs └── daum.mjs /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fallroot/reported-by/HEAD/icon-128.png -------------------------------------------------------------------------------- /screenshot/naver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fallroot/reported-by/HEAD/screenshot/naver.png -------------------------------------------------------------------------------- /loader.js: -------------------------------------------------------------------------------- 1 | (async () => { 2 | const hostname = window.location.hostname 3 | let service 4 | 5 | if (hostname.includes('v.daum.net')) { 6 | service = 'daum' 7 | } else if (hostname.includes('news.naver.com')) { 8 | service = 'naver' 9 | } else { 10 | return 11 | } 12 | 13 | const src = window.chrome.extension.getURL(`${service}.mjs`) 14 | const naver = await import(src) 15 | naver.run() 16 | })() 17 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "content_scripts": [ 3 | { 4 | "matches": [ 5 | "https://*.v.daum.net/*", 6 | "https://news.naver.com/*" 7 | ], 8 | "js": [ 9 | "loader.js" 10 | ] 11 | } 12 | ], 13 | "description": "지금 보고 있는 기사를 작성한 기자의 다른 기사 목록", 14 | "icons": { 15 | "128": "icon-128.png" 16 | }, 17 | "manifest_version": 2, 18 | "name": "이 기자의 다른 기사", 19 | "permissions": [ 20 | "https://search.daum.net/*", 21 | "https://search.naver.com/*" 22 | ], 23 | "version": "0.1.0", 24 | "web_accessible_resources": [ 25 | "daum.mjs", 26 | "naver.mjs", 27 | "util.mjs" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reported-by", 3 | "version": "0.0.3", 4 | "description": "이 기자의 다른 기사", 5 | "main": "main.js", 6 | "dependencies": {}, 7 | "devDependencies": { 8 | "babel-eslint": "^10.0.1", 9 | "eslint": "^5.13.0", 10 | "standard": "^12.0.1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/fallroot/reported-by.git" 15 | }, 16 | "author": "CK Moon ", 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/fallroot/reported-by/issues" 20 | }, 21 | "homepage": "https://github.com/fallroot/reported-by", 22 | "standard": { 23 | "parser": "babel-eslint" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /util.mjs: -------------------------------------------------------------------------------- 1 | function parseEmails (text) { 2 | const EMAIL_RE = /\b[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}/gi 3 | const result = [] 4 | let match 5 | 6 | while ((match = EMAIL_RE.exec(text))) { 7 | const email = match[0] 8 | const bunch = text.substring(Math.max(0, match.index - 20), Math.min(match.index + email.length + 20, text.length - 1)) 9 | 10 | result.push({ 11 | email, 12 | name: parseName(bunch) 13 | }) 14 | } 15 | 16 | return result 17 | } 18 | 19 | function parseName (text) { 20 | const REPORTER_RE = /(?<=[^가-힣])([가-힣]+)\s*(기자|특파원)/g 21 | const matches = REPORTER_RE.exec(text) 22 | 23 | if (matches) { 24 | return `${matches[1]} ${matches[2]}` 25 | } 26 | } 27 | 28 | function sanitizeHTML (value) { 29 | const el = document.createElement('div') 30 | el.textContent = value 31 | return el.innerHTML 32 | } 33 | 34 | export { 35 | parseEmails, 36 | sanitizeHTML 37 | } 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

이 기자의 다른 기사

2 | 3 | # 이 기자의 다른 기사 4 | 5 | 지금 보고 있는 기사를 작성한 기자의 다른 기사 목록을 보여주는 크롬 브라우저의 확장 프로그램 6 | 7 | ![Software License](https://img.shields.io/github/license/fallroot/reported-by.svg?style=flat) 8 | 9 | ## 설치 10 | 11 | [크롬 웹 스토어](https://chrome.google.com/webstore/detail/%EC%9D%B4-%EA%B8%B0%EC%9E%90%EC%9D%98-%EB%8B%A4%EB%A5%B8-%EA%B8%B0%EC%82%AC/klonfbaobgdkldnmjmjjlkkkhnialfij)를 통해 확장 프로그램을 설치한다. 12 | 13 | ## 지원하는 서비스 14 | 15 |

네이버 뉴스 지원

16 | 17 | - 다음 뉴스 (v0.1.0) 18 | - 네이버 뉴스 (v0.0.1) 19 | 20 | ## 아이콘 21 | 22 |
Icons made by Freepik from www.flaticon.com is licensed by CC 3.0 BY
23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 CK Moon 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 | -------------------------------------------------------------------------------- /naver.mjs: -------------------------------------------------------------------------------- 1 | import { parseEmails, sanitizeHTML } from './util.mjs' 2 | 3 | function run () { 4 | const content = getTextContent() 5 | 6 | if (!content) { 7 | return 8 | } 9 | 10 | const emails = parseEmails(content) 11 | 12 | if (!emails.length) { 13 | return 14 | } 15 | 16 | fetchArticles(emails[emails.length - 1]) 17 | } 18 | 19 | function getTextContent () { 20 | const el = document.querySelector('#articleBodyContents') 21 | 22 | if (el) { 23 | return el.textContent 24 | } 25 | } 26 | 27 | function fetchArticles (data) { 28 | const url = `https://search.naver.com/search.naver?where=news&query=${encodeURIComponent(data.email)}&sort=1` 29 | 30 | window.fetch(url).then(response => { 31 | return response.text() 32 | }).then(body => { 33 | data.url = url 34 | generateList(body, data) 35 | }) 36 | } 37 | 38 | function generateList (text, data) { 39 | const list = parseList(text) 40 | 41 | if (!list || !list.length) { 42 | return 43 | } 44 | 45 | const fragment = document.createDocumentFragment() 46 | 47 | list.forEach(item => { 48 | const li = document.createElement('li') 49 | 50 | li.innerHTML = `${item.title}` 51 | fragment.appendChild(li) 52 | }) 53 | 54 | const title = data.name ? `${data.name} ${data.email}` : data.email 55 | const div = document.createElement('div') 56 | 57 | div.className = 'section' 58 | div.innerHTML = `

${title}

더보기
` 59 | div.querySelector('ul').appendChild(fragment) 60 | document.querySelector('table.container td.aside div.aside').insertBefore(div, document.querySelector('.section:first-child')) 61 | } 62 | 63 | function parseList (text) { 64 | const LIST_RE = /