// ==UserScript==
// @name AC-独家-淘宝天猫优惠券查询领取,大额优惠券,【100元购物神券】,省钱购物,领券购买更优惠,平均优惠20%
// @version 8.1
// @description 独家查询淘宝商品查询是否具有优惠券,各种大额优惠券,【3元|10元|20元|40元】优惠券领取,购物必备,特大优惠
// @author AC
// @include https://item.taobao.com/item.htm*
// @include https://detail.tmall.com/item.htm*
// @include https://s.taobao.com/search*
// @include https://cart.taobao.com/*
// @include *://uland.taobao.com/coupon/*
// @note 2021.12.20-V8.1 修复由于标题的问题导致的获取不到的问题
// @note 2021.12.13-V8.0 重写架构,简单页面效果即可
// @note 2017.11.17-V3.0 修复上一版过于流畅的bug,上一版更新导致的bug挺多的。。。
// @note 2017.11.17-V2.9 正常更新,尽量减少由于重定向带来的影响,同时修正规则避免出事
// @note 2017.11.17-V2.8 上个版本更新导致的bug ...P_P...
// @note 2017.11.17-V2.7 修复优惠券获取不正确的问题,以及部分情况下无法显示优惠券的问题
// @note 2017.11.12-V2.6 简单更新
// @note 2017.11.11-V2.5 查询速度更快速,并且优化了载入情况,加入了个人数据
// @note 2017.11.10-V2.4 如果有优惠,可以展示出优惠信息了
// @note 2017.11.4-V2.3 暂时新增两种模式供选择
// @note 2017.11.2-V2.2 参照了一个大神的脚本之后拿到了一些css,于是又加了些东西,展示一些双十一相关的标签
// @note 2017.11.1-V2.1 切换为include规则,而非match规则,避免GreaseMonkey上无法使用的问题
// @note 2017.10.30-V2.0 修复在某些页面上,标题获取不正确的问题
// @note 2017.10.28-V1.0 第一版本,edit from https://greasyfork.org/zh-TW/scripts/34604
// @icon https://gitee.com/remixAC/GM_script/raw/master/images/head.jpg
// @home-url https://greasyfork.org/zh-TW/scripts/34606
// @run-at document-end
// @namespace 1353464539@qq.com
// @antifeature referral-link 含有优惠券信息以及其对应的返利链接
// @connect api.ntaow.com
// @grant GM_xmlhttpRequest
// @license GPL-3.0-only
// ==/UserScript==
// 在详情页面查询优惠券,如果明显有优惠券,那么增加页面地址,并且可以跳转
const MyConfig = {
name: '',
id: '',
shopTitle: '',
couponPrice: 0,
originalPrice: 0,
isValid: () => {
return /\d+/.test(MyConfig.id)
}
}
function init() {
const insertCSS = `
.ac-btn-father{
font-size: 32px;
font-weight: bold;
font-family: "microsoft yahei";
}
.ac-btn{
opacity: 0.85;
color: #f95f52;
cursor: pointer;
}
.ac-btn:hover{
color: #FFC800D8 !important;
text-shadow: 0 0 rgba(242,33,49,30),0 0 0 rgba(242,33,49,30),0 1px 1px rgba(242,33,49,30),1px 0 1px rgba(242,33,49,30),-1px 0 1px rgba(242,33,49,30),0 0 1px rgba(242,33,49,30) !important;
}
`;
const insertJS = `
const openUrl = function(node){
if(node.dataset.url.indexOf("javascript:void") < 0) window.open(node.dataset.url);
};
`;
const insertHTML = `
<div class="ac-btn-father">
<span class="ac-btn ac-btn-click" data-url="javascript:void(0);" onclick="openUrl(this)">
查找中
</span>
<span class="ac-btn ac-btn-click" data-url="" onclick="openUrl(this)">
[站内搜]
</span>
<span class="ac-btn ac-btn-click" data-url="" onclick="openUrl(this)">
[找相似]
</span>
</div>
`
MyApi.addStyle(insertCSS)
MyApi.addScript(insertJS)
MyApi.safeWaitFunc('[class*="ItemHeader--mainTitle"]', node => {
node.insertAdjacentHTML('afterend', insertHTML);
setTimeout(() => {
initSite()
}, 100)
})
MyApi.safeWaitFunc('[class*="tb-main-title"]', node => {
node.insertAdjacentHTML('afterend', insertHTML);
setTimeout(() => {
initSite()
}, 100)
})
}
function initSite() {
MyConfig.id = MyApi.getUrlAttribute('id')
MyConfig.name = document.querySelector('.tb-main-title, h1[class*="ItemHeader--mainTitle"]').innerText.trim()
resetUrls(getUrls())
checkCoupon()
function reInitConfig() {
// 淘宝、天猫、老版淘宝
try{
MyConfig.originalPrice = document.querySelector('#J_PromoPriceNum, .tb-rmb-num, [class*="priceText"]').innerText.trim().split('-')[0]
MyConfig.shopTitle = document.querySelector('.tb-shop-name, .shop-name-link, [class*="ShopHeader--title"]').innerText.trim()
}catch (e) {}
}
function getUrls() {
const findUrl = 'https://www.ntaow.com/coupon.html?mQuery=' + encodeURIComponent(MyConfig.name)
const innerUrl = 'https://s.taobao.com/search?q=' + encodeURIComponent(MyConfig.name)
const similarUrl = 'https://www.ntaow.com/coupon.html?mQuery=' + encodeURIComponent(MyConfig.name)
return [
findUrl,
innerUrl,
similarUrl
]
}
function resetUrls(urls = []) {
try{
document.querySelectorAll('.ac-btn-click').forEach((one, index) => {
one.setAttribute('data-url', urls[index])
})
}catch (e){}
}
function resetFindTitle(newTitle) {
document.querySelectorAll('.ac-btn-click')[0].innerText = newTitle
}
async function checkCoupon() {
if (MyConfig.isValid()) {
const [err, res] = await MyApi.http.get(`https://api.ntaow.com/api/coupon/tran?id=${MyConfig.id}&title=${MyConfig.name}`)
if (!err) {
reInitConfig()
const couponList = JSON.parse(res)
const couponElement = findBestCoupon(couponList) || {}
const {
quan= '', // 优惠券价格
shop_title = '', // 店家名字
price= '' // 商品价格预期
} = couponElement
const couponPrice = +quan
// MyConfig.name = title
MyConfig.couponPrice = couponPrice
if(couponPrice > 0) {
resetFindTitle(`!${couponPrice}元优惠券!`)
} else {
resetFindTitle(`无优惠券`)
}
resetUrls(getUrls())
}
}
}
function findBestCoupon(couponList) {
for (const couponElement of couponList) {
const {
quan= '', // 优惠券价格
shop_title = '', // 店家名字
price= '' // 商品价格预期
} = couponElement
if(shop_title === MyConfig.shopTitle && price === MyConfig.originalPrice) {
return couponElement
}
}
return couponList.length && couponList[0]
}
}
const MyApi = (() => {
function addStyle(css, className = '', isReload = false){ // 添加CSS代码,不考虑文本载入时间,带有className
var tout = setInterval(function(){
if(document.body != null){
clearInterval(tout);
if(className) {
// 节点不存在,或者是准备覆盖的时候
if(isReload === false && document.querySelector("."+className) != null){return;}
// 节点已经存在,并且不准备覆盖
try {document.querySelector("."+className).remove();}catch (e){}
}
const cssNode = document.createElement("style");
if(className) {
cssNode.className = className;
}
cssNode.innerHTML = css;
try{document.body.appendChild(cssNode);}catch (e){console.log(e.message);}
}
}, 200);
}
function addScript(scriptInner) {
const scriptNode = document.createElement('script')
scriptNode.innerText = scriptInner
document.head.appendChild(scriptNode)
}
const safeWaitFunc = (selector, callbackFunc = node => {}, findTick = 200, clearAfterFind = true, timeout = 20000 * 1000) => {
let count = timeout / findTick
const t_id = setInterval(function() {
if(count-- < 0) {
clearInterval(t_id)
}
if ((typeof (selector) == "string")) {
let selectRes = document.querySelectorAll(selector);
if (selectRes.length <= 0) return
if (selectRes.length === 1) selectRes = selectRes[0];
if (clearAfterFind) clearInterval(t_id);
callbackFunc(selectRes);
} else if (typeof (selector) === "function") {
const res = selector()
if(res.length > 0) {
if (clearAfterFind) clearInterval(t_id);
callbackFunc(selector()[0]);
} else if(res) {
if (clearAfterFind) clearInterval(t_id);
callbackFunc();
}
}
}, findTick);
}
function getUrlAttribute(attribute, needDecode = true){
var searchValue = (window.location.search.substr(1) + "").split("&");
for (var i = 0; i < searchValue.length; i++) {
var key_value = searchValue[i].split("=");
var reg = new RegExp("^"+attribute+"$");
if (reg.test(key_value[0])) {
var searchWords = key_value[1];
return needDecode?decodeURIComponent(searchWords):searchWords;
}
}
}
const http = {
async get(url) {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
url,
method: 'GET',
timeout: 10000,
onload: resp => resolve([null, resp.responseText]),
onerror: resp => reject([resp, {}])
})
})
},
async post(url, data) {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
url,
data,
method: 'POST',
timeout: 10000,
onload: resp => resolve([null, resp.responseText]),
onerror: resp => reject([resp, {}])
})
})
}
}
return {
addStyle,
addScript,
safeWaitFunc,
getUrlAttribute,
http
}
})()
if(typeof(acTB) == "undefined"){
acTB = 1;
init()
}