Go로 단축 URL 자동 정리
25995 단어 showdevgoclipboardurlshorteners
utm_*
등과 같은 추적 매개변수로 가득 차 있어 그늘이 없는 방식으로 URL을 공유하기가 상대적으로 어렵습니다.웹 브라우저를 계속 사용하고 있다면 더 이상 문제가 되지 않습니다. 리디렉터의 전부는 아니더라도 대부분을 제거하는 다양한 브라우저 확장이 도착하면 이 문제가 해결됩니다. 여전히 다루지 않는 것은 시스템 클립보드입니다. 대화 상대에게 다음과 같은 링크 보내기
https://bit.ly/3hXl0mS
아마도 이것을 보내는 것보다 덜 매력적인 옵션일 것입니다:
https://dev.to/tux0r/properly-validating-e-mail-addresses-3lpj
채워야 할 틈새 시장이 있습니다. 그리고 그렇게 하는 것은 비교적 쉽습니다! Go를 사용해 봅시다. 왜 안 될까요?
우리의 애플리케이션은 두 부분으로 구성됩니다. 하나는 클립보드를 감시하고(사용자가 원할 때마다 중지할 수 있음), 다른 하나는 URL을 처리하고 단축 및 추적 해제합니다. 두 번째는 더 복잡합니다.
첫 번째 단계: 클리너 기능.
단축된 URL에는 bit.ly
및 t.co
와 같은 실제 리다이렉터와 자체 링크를 시도하는 회사에 속하는 의사 리디렉터(예: 대부분의 대형 미디어 기업. 애플리케이션은 둘 다 감지해야 합니다. 운 좋게도 Go에는 다양한 네트워크 기능이 있으므로 외부 패키지 없이 URL의 실제 대상을 결정하는 함수를 작성할 수 있습니다.
func ExpandUrl(url string) (string, error) {
// URL expander with x509 checks disabled.
expandedUrl := url
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
expandedUrl = req.URL.String()
return nil
},
Transport: tr,
}
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return "", err
}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
return expandedUrl, nil
}
이것은 실제 URL을 반환하거나 문제가 있는 경우 오류를 반환합니다. 사이트 자체의 유효성은 우리가 달성하고자 하는 것과 관련이 없기 때문에 인증서 확인을 건너뛸 것입니다.
물론 알려진 단축기 목록을 유지할 수도 있습니다. URL이 리디렉션임을 이미 알고 있다면 서버를 적극적으로 호출할 필요가 없습니다.
var shortenerList = []string{
"bit.ly", "buff.ly", "dlvr.it",
"goo.gl", "youtu.be", "tinyurl.com",
"ow.ly", "amzn.to", "ift.tt", "zpr.io",
"apple.co", "mol.im", "redd.it",
"shar.es", "is.gd", "dld.bz",
"trib.al", "fb.me", "tumblr.co",
"cutt.ly", "app.link", "twib.in",
"kko.to", "rsci.app.link", "upflow.co",
"snip.ly", "lnk.to", "1jux.net", "gscoff.co",
}
이 목록은 불완전하지만 직접 확장하는 방법을 볼 수 있습니다. 그런데 목록 내부의 주석은 허용되므로 자유롭게 구조를 추가할 수 있습니다.
목록에 대해 이야기하면서 추적 매개변수도 제거하고 싶습니다. 이를 위해 애플리케이션은 어떤 매개변수가 원하지 않는지 알아야 합니다. 오늘 현재 내 것은 다음과 같습니다.
var urlParamBlacklist = []string{
"wtmc", "WT.mc_id", "wt_zmc",
"ocid", "xid",
"at_medium", "at_campaign", "at_custom1", "at_custom2",
"at_custom3", "at_custom4",
"utm_source", "utm_medium", "utm_campaign", "utm_term",
"utm_content", "utm_name", "utm_referrer", "utm_brand",
"utm_social-type", "utm_kxconfid",
"guce_referrer", "guce_referrer_sig", "guccounter",
"ga_source", "ga_medium", "ga_term", "ga_content",
"ga_campaign", "ga_place",
"fb_action_ids", "fb_action_types", "fb_source", "fb_ref",
"fbclid", "fbc",
"hmb_campaign", "hmb_medium", "hmb_source",
"newsticker", "CMP", "feature", "camp", "cid", "source",
"ns_campaign", "ns_mchannel", "ito", "xg_source", "__tn__",
"__twitter_impression", "share", "ncid", "rnd", "feed_id",
"_unique_id", "GEPC", "pt", "xtor", "wtrid", "medium", "sara_ecid",
"from", "inApp", "ssm", "campaign", "mbid", "s_campaign", "rss_id",
"cmpid", "s_cid", "mv2", "scid", "sdid", "s_iid", "ssm",
"spi_ref", "referrerlane",
"share_bandit_exp", "share_bandit_var",
"igshid", "idpartenaire",
"aff_code", "affID",
"recruited_by_id", "recruiter",
}
이제 우리에게 필요한 것은 URL을 매개변수로 받아 단축되지 않은 추적되지 않은 것을 반환하는 래퍼 함수입니다.
func contains(arr []string, str string) bool {
// Array-containing check: Returns true if found.
// I wish Go could do that natively. :-)
for _, a := range arr {
if a == str {
return true
}
}
return false
}
func processUrlItem(urlToCheck string) string {
// Processes an URL item, returns the cleaned, unshortened
// "expanded" URL.
u, err := url.Parse(urlToCheck)
if err != nil {
log.Fatal(err)
}
// Some URL shorteners are not known to us (yet?).
// Chances are that URLs with a path that ends in
// "/SoMeSTRiNG123" are shortened. Catch them as well.
re, _ := regexp.Compile("^/[A-Za-z0-9_-]{5,}$")
potentialUrlShortener := re.MatchString(u.Path)
if potentialUrlShortener || contains(shortenerList, u.Hostname()) {
expandedUrl, err := ExpandUrl(urlToCheck)
if err != nil {
// Cannot reach the URL:
return urlToCheck
}
// Overwrite the original URL by the expanded one:
urlToCheck = expandedUrl
// Parse again, just in case:
u, err = url.Parse(urlToCheck)
if err != nil {
// Error in the updated domain:
return urlToCheck
}
}
// Remove tracking parameters:
q := u.Query()
for _, param := range urlParamBlacklist {
q.Del(param)
u.RawQuery = q.Encode()
}
return u.String()
}
작동 여부를 테스트하시겠습니까? 임시main()
함수 작성:
func main() {
fmt.Printf("%s\n", processUrlItem("https://bit.ly/3hXl0mS"))
}
아무도 끔찍한 실수를 하지 않았다면 지금 터미널에 단축되지 않은 링크가 표시되어야 합니다.
다음 단계: 실제 적용.
이제 정리 기능이 작동하므로 클립보드를 둘러쌀 수 있습니다. 이것도 비교적 쉽습니다. 클립보드를 1초에 한 번 폴링하고 내용을 확인하고 발견된 모든 URL을 클리너에 전달하고 결과를 클립보드에 다시 쓸 수 있습니다. Go의 좋은 생태계는 이것을 훨씬 더 쉽게 해줍니다. 우리는 주석과 공백을 포함하여 전체 호출 애플리케이션을 50줄 미만으로 맞출 수 있습니다.
package main
import (
"fmt"
"net/url"
"time"
"github.com/getlantern/systray"
"github.com/atotto/clipboard"
)
var (
previousUrl string // Avoid parsing it over and over again
)
func main() {
go func() {
for x := range time.Tick(time.Second) {
clipped, err := clipboard.ReadAll()
if err == nil && clipped != previousUrl {
u, invalidUrl := url.Parse(clipped)
if invalidUrl == nil && u.Host != "" {
// valid URL
fmt.Printf("[%s] Processing URL: %s\n", x, clipped)
previousUrl = processUrlItem(clipped)
clipboard.WriteAll(previousUrl)
}
}
}
}()
systray.Run(onReady, onExit)
}
func onReady() {
systray.SetTitle("🧹")
systray.SetTooltip("I'm cleaning URLs in the clipboard")
mQuitOrig := systray.AddMenuItem("Quit", "Quit cleaning URLs")
go func() {
<-mQuitOrig.ClickedCh
systray.Quit()
}()
}
func onExit() {
// Cleanup
// This is pointless as of now, but might happen later.
}
트레이 아이콘도 함께 제공됩니다! (그림 이모티콘은 Windows에서는 작동하지 않지만 macOS에서는 멋지게 보입니다.)
작업 예
위의 글은 서론에서 말했던 문제에 대한 나의 작업의 결과이다. 코드를 직접 작성하기 전에 테스트하려면 저장소에서 바로 가져오세요.
% mkdir work
% fossil clone https://code.rosaelefanten.org/clipurlcleaner clip.fossil
% cd work ; fossil open ../clip.fossil
(GitHub 미러는 here. 입니다)
빌드 및 실행:
% go build
% ./clipurlcleaner >/dev/null &
macOS Catalina 및 Windows 10에서 솔루션을 테스트했으며 충분히 잘 작동합니다. Linux, BSD 또는 Unix에서 작동하는지 여부는 말할 수 없습니다. 작동하지 않는 경우 수정하도록 도와주세요.
코멘트?
직원들이 저를 싫어하기 때문에 슬프게도 여기 DEV에서 답변할 수 없습니다. 읽어 주셔서 감사합니다!
Reference
이 문제에 관하여(Go로 단축 URL 자동 정리), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/tux0r/auto-magically-cleaning-up-shortened-urls-with-go-1fgh
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
func ExpandUrl(url string) (string, error) {
// URL expander with x509 checks disabled.
expandedUrl := url
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
expandedUrl = req.URL.String()
return nil
},
Transport: tr,
}
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return "", err
}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
return expandedUrl, nil
}
var shortenerList = []string{
"bit.ly", "buff.ly", "dlvr.it",
"goo.gl", "youtu.be", "tinyurl.com",
"ow.ly", "amzn.to", "ift.tt", "zpr.io",
"apple.co", "mol.im", "redd.it",
"shar.es", "is.gd", "dld.bz",
"trib.al", "fb.me", "tumblr.co",
"cutt.ly", "app.link", "twib.in",
"kko.to", "rsci.app.link", "upflow.co",
"snip.ly", "lnk.to", "1jux.net", "gscoff.co",
}
var urlParamBlacklist = []string{
"wtmc", "WT.mc_id", "wt_zmc",
"ocid", "xid",
"at_medium", "at_campaign", "at_custom1", "at_custom2",
"at_custom3", "at_custom4",
"utm_source", "utm_medium", "utm_campaign", "utm_term",
"utm_content", "utm_name", "utm_referrer", "utm_brand",
"utm_social-type", "utm_kxconfid",
"guce_referrer", "guce_referrer_sig", "guccounter",
"ga_source", "ga_medium", "ga_term", "ga_content",
"ga_campaign", "ga_place",
"fb_action_ids", "fb_action_types", "fb_source", "fb_ref",
"fbclid", "fbc",
"hmb_campaign", "hmb_medium", "hmb_source",
"newsticker", "CMP", "feature", "camp", "cid", "source",
"ns_campaign", "ns_mchannel", "ito", "xg_source", "__tn__",
"__twitter_impression", "share", "ncid", "rnd", "feed_id",
"_unique_id", "GEPC", "pt", "xtor", "wtrid", "medium", "sara_ecid",
"from", "inApp", "ssm", "campaign", "mbid", "s_campaign", "rss_id",
"cmpid", "s_cid", "mv2", "scid", "sdid", "s_iid", "ssm",
"spi_ref", "referrerlane",
"share_bandit_exp", "share_bandit_var",
"igshid", "idpartenaire",
"aff_code", "affID",
"recruited_by_id", "recruiter",
}
func contains(arr []string, str string) bool {
// Array-containing check: Returns true if found.
// I wish Go could do that natively. :-)
for _, a := range arr {
if a == str {
return true
}
}
return false
}
func processUrlItem(urlToCheck string) string {
// Processes an URL item, returns the cleaned, unshortened
// "expanded" URL.
u, err := url.Parse(urlToCheck)
if err != nil {
log.Fatal(err)
}
// Some URL shorteners are not known to us (yet?).
// Chances are that URLs with a path that ends in
// "/SoMeSTRiNG123" are shortened. Catch them as well.
re, _ := regexp.Compile("^/[A-Za-z0-9_-]{5,}$")
potentialUrlShortener := re.MatchString(u.Path)
if potentialUrlShortener || contains(shortenerList, u.Hostname()) {
expandedUrl, err := ExpandUrl(urlToCheck)
if err != nil {
// Cannot reach the URL:
return urlToCheck
}
// Overwrite the original URL by the expanded one:
urlToCheck = expandedUrl
// Parse again, just in case:
u, err = url.Parse(urlToCheck)
if err != nil {
// Error in the updated domain:
return urlToCheck
}
}
// Remove tracking parameters:
q := u.Query()
for _, param := range urlParamBlacklist {
q.Del(param)
u.RawQuery = q.Encode()
}
return u.String()
}
func main() {
fmt.Printf("%s\n", processUrlItem("https://bit.ly/3hXl0mS"))
}
이제 정리 기능이 작동하므로 클립보드를 둘러쌀 수 있습니다. 이것도 비교적 쉽습니다. 클립보드를 1초에 한 번 폴링하고 내용을 확인하고 발견된 모든 URL을 클리너에 전달하고 결과를 클립보드에 다시 쓸 수 있습니다. Go의 좋은 생태계는 이것을 훨씬 더 쉽게 해줍니다. 우리는 주석과 공백을 포함하여 전체 호출 애플리케이션을 50줄 미만으로 맞출 수 있습니다.
package main
import (
"fmt"
"net/url"
"time"
"github.com/getlantern/systray"
"github.com/atotto/clipboard"
)
var (
previousUrl string // Avoid parsing it over and over again
)
func main() {
go func() {
for x := range time.Tick(time.Second) {
clipped, err := clipboard.ReadAll()
if err == nil && clipped != previousUrl {
u, invalidUrl := url.Parse(clipped)
if invalidUrl == nil && u.Host != "" {
// valid URL
fmt.Printf("[%s] Processing URL: %s\n", x, clipped)
previousUrl = processUrlItem(clipped)
clipboard.WriteAll(previousUrl)
}
}
}
}()
systray.Run(onReady, onExit)
}
func onReady() {
systray.SetTitle("🧹")
systray.SetTooltip("I'm cleaning URLs in the clipboard")
mQuitOrig := systray.AddMenuItem("Quit", "Quit cleaning URLs")
go func() {
<-mQuitOrig.ClickedCh
systray.Quit()
}()
}
func onExit() {
// Cleanup
// This is pointless as of now, but might happen later.
}
트레이 아이콘도 함께 제공됩니다! (그림 이모티콘은 Windows에서는 작동하지 않지만 macOS에서는 멋지게 보입니다.)
작업 예
위의 글은 서론에서 말했던 문제에 대한 나의 작업의 결과이다. 코드를 직접 작성하기 전에 테스트하려면 저장소에서 바로 가져오세요.
% mkdir work
% fossil clone https://code.rosaelefanten.org/clipurlcleaner clip.fossil
% cd work ; fossil open ../clip.fossil
(GitHub 미러는 here. 입니다)
빌드 및 실행:
% go build
% ./clipurlcleaner >/dev/null &
macOS Catalina 및 Windows 10에서 솔루션을 테스트했으며 충분히 잘 작동합니다. Linux, BSD 또는 Unix에서 작동하는지 여부는 말할 수 없습니다. 작동하지 않는 경우 수정하도록 도와주세요.
코멘트?
직원들이 저를 싫어하기 때문에 슬프게도 여기 DEV에서 답변할 수 없습니다. 읽어 주셔서 감사합니다!
Reference
이 문제에 관하여(Go로 단축 URL 자동 정리), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/tux0r/auto-magically-cleaning-up-shortened-urls-with-go-1fgh
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
% mkdir work
% fossil clone https://code.rosaelefanten.org/clipurlcleaner clip.fossil
% cd work ; fossil open ../clip.fossil
% go build
% ./clipurlcleaner >/dev/null &
직원들이 저를 싫어하기 때문에 슬프게도 여기 DEV에서 답변할 수 없습니다. 읽어 주셔서 감사합니다!
Reference
이 문제에 관하여(Go로 단축 URL 자동 정리), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/tux0r/auto-magically-cleaning-up-shortened-urls-with-go-1fgh텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)