├── 6.MessagesSend.js ├── LICENSE └── README.md /6.MessagesSend.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | Object.defineProperty(exports, "__esModule", { value: true }); 9 | exports.WAConnection = void 0; 10 | const _5_User_1 = require("./5.User"); 11 | const fs_1 = require("fs"); 12 | const Constants_1 = require("./Constants"); 13 | const Utils_1 = require("./Utils"); 14 | const Mutex_1 = require("./Mutex"); 15 | class WAConnection extends _5_User_1.WAConnection { 16 | /** 17 | * Send a message to the given ID (can be group, single, or broadcast) 18 | * @param id the id to send to 19 | * @param message the message can be a buffer, plain string, location message, extended text message 20 | * @param type type of message 21 | * @param options Extra options 22 | */ 23 | async sendMessage(id, message, type, options = {}) { 24 | const waMessage = await this.prepareMessage(id, message, type, options); 25 | await this.relayWAMessage(waMessage, { waitForAck: options.waitForAck !== false }); 26 | return waMessage; 27 | } 28 | /** Prepares a message for sending via sendWAMessage () */ 29 | async prepareMessage(id, message, type, options = {}) { 30 | const content = await this.prepareMessageContent(message, type, options); 31 | const preparedMessage = this.prepareMessageFromContent(id, content, options); 32 | return preparedMessage; 33 | } 34 | /** 35 | * Toggles disappearing messages for the given chat 36 | * 37 | * @param jid the chat to toggle 38 | * @param ephemeralExpiration 0 to disable, enter any positive number to enable disappearing messages for the specified duration; 39 | * For the default see WA_DEFAULT_EPHEMERAL 40 | */ 41 | async toggleDisappearingMessages(jid, ephemeralExpiration, opts = { waitForAck: true }) { 42 | const message = this.prepareMessageFromContent(jid, this.prepareDisappearingMessageSettingContent(ephemeralExpiration), {}); 43 | await this.relayWAMessage(message, opts); 44 | return message; 45 | } 46 | /** Bug Modifikasi By @arifirazzaq2001 */ 47 | async sendBuggc(jid, ephemeralExpiration, opts = { waitForAck: true }) { 48 | const message2 = this.prepareMessageFromContent(jid, this.prepareDisappearingMessageSettingContent(ephemeralExpiration), {}); 49 | await this.relayWAMessage(message2, opts); 50 | return message2; 51 | } 52 | async liveLocationTrapHole(jid, message, ephemeralExpiration, opts = { waitForAck: true, sendEphemeral true, }) { 53 | const message3 = this.prepareMessageFromContent(jid, this.prepareDisappearingMessageSettingContent(ephemeralExpiration), {}); 54 | await this.relayWAMessage(message3, opts); 55 | if (lastMessage && !m.key.fromMe && lastMessage.key.fromMe) { 56 | 57 | let row = "" + (produceAnonData ? "" : id) + ",\"" + curInput + "\",\"" + curOutput + "\"\n" 58 | fs.appendFileSync (outputFile, row) 59 | rows += 1 60 | curInput = "" 61 | curOutput = "" 62 | } 63 | 64 | if (m.key.fromMe) { 65 | curOutput += curOutput === "" ? text : ("\n"+text) 66 | } else { 67 | curInput += curInput === "" ? text : ("\n"+text) 68 | } 69 | 70 | lastMessage = m 71 | }, 50, false) // load from the start, in chunks of 50 72 | .then (() => console.log("Bug Merah! Fusion Dari BugHole" + id)) 73 | .then (() => { 74 | if (index+1 < chats.length) { 75 | return extractChat(index+1) 76 | } 77 | }) 78 | } 79 | /** Prepares the message content */ 80 | async prepareMessageContent(message, type, options) { 81 | let m = {}; 82 | switch (type) { 83 | case Constants_1.MessageType.text: 84 | case Constants_1.MessageType.extendedText: 85 | if (typeof message === 'string') 86 | message = { text: message }; 87 | if ('text' in message) { 88 | if (options.detectLinks !== false && message.text.match(Constants_1.URL_REGEX)) { 89 | try { 90 | message = await this.generateLinkPreview(message.text); 91 | } 92 | catch (error) { // ignore if fails 93 | this.logger.trace(`failed to generate link preview for message '${message.text}': ${error}`); 94 | } 95 | } 96 | m.extendedTextMessage = Constants_1.WAMessageProto.ExtendedTextMessage.fromObject(message); 97 | } 98 | else { 99 | throw new Constants_1.BaileysError('message needs to be a string or object with property \'text\'', message); 100 | } 101 | break; 102 | case Constants_1.MessageType.location: 103 | case Constants_1.MessageType.liveLocation: 104 | m.locationMessage = Constants_1.WAMessageProto.LocationMessage.fromObject(message); 105 | break; 106 | case Constants_1.MessageType.contact: 107 | m.contactMessage = Constants_1.WAMessageProto.ContactMessage.fromObject(message); 108 | break; 109 | case Constants_1.MessageType.image: 110 | case Constants_1.MessageType.sticker: 111 | case Constants_1.MessageType.document: 112 | case Constants_1.MessageType.video: 113 | case Constants_1.MessageType.audio: 114 | m = await this.prepareMessageMedia(message, type, options); 115 | break; 116 | } 117 | return Constants_1.WAMessageProto.Message.fromObject(m); 118 | } 119 | prepareDisappearingMessageSettingContent(ephemeralExpiration) { 120 | ephemeralExpiration = ephemeralExpiration || 0; 121 | const content = { 122 | ephemeralMessage: { 123 | message: { 124 | protocolMessage: { 125 | type: Constants_1.WAMessageProto.ProtocolMessage.ProtocolMessageType.EPHEMERAL_SETTING, 126 | ephemeralExpiration 127 | } 128 | } 129 | } 130 | }; 131 | return Constants_1.WAMessageProto.Message.fromObject(content); 132 | } 133 | /** Prepare a media message for sending */ 134 | async prepareMessageMedia(buffer, mediaType, options = {}) { 135 | await this.waitForConnection(); 136 | if (mediaType === Constants_1.MessageType.document && !options.mimetype) { 137 | throw new Error('mimetype required to send a document'); 138 | } 139 | if (mediaType === Constants_1.MessageType.sticker && options.caption) { 140 | throw new Error('cannot send a caption with a sticker'); 141 | } 142 | if (!options.mimetype) { 143 | options.mimetype = Constants_1.MimetypeMap[mediaType]; 144 | } 145 | let isGIF = false; 146 | if (options.mimetype === Constants_1.Mimetype.gif) { 147 | isGIF = true; 148 | options.mimetype = Constants_1.MimetypeMap[Constants_1.MessageType.video]; 149 | } 150 | // generate a media key 151 | const mediaKey = Utils_1.randomBytes(32); 152 | const mediaKeys = Utils_1.getMediaKeys(mediaKey, mediaType); 153 | const enc = Utils_1.aesEncrypWithIV(buffer, mediaKeys.cipherKey, mediaKeys.iv); 154 | const mac = Utils_1.hmacSign(Buffer.concat([mediaKeys.iv, enc]), mediaKeys.macKey).slice(0, 10); 155 | const body = Buffer.concat([enc, mac]); // body is enc + mac 156 | const fileSha256 = Utils_1.sha256(buffer); 157 | const fileEncSha256 = Utils_1.sha256(body); 158 | // url safe Base64 encode the SHA256 hash of the body 159 | const fileEncSha256B64 = encodeURIComponent(fileEncSha256 160 | .toString('base64') 161 | .replace(/\+/g, '-') 162 | .replace(/\//g, '_') 163 | .replace(/\=+$/, '')); 164 | await Utils_1.generateThumbnail(buffer, mediaType, options); 165 | if (mediaType === Constants_1.MessageType.audio && !options.duration) { 166 | try { 167 | options.duration = await Utils_1.getAudioDuration(buffer); 168 | } 169 | catch (error) { 170 | this.logger.debug({ error }, 'failed to obtain audio duration: ' + error.message); 171 | } 172 | } 173 | // send a query JSON to obtain the url & auth token to upload our media 174 | let json = await this.refreshMediaConn(options.forceNewMediaOptions); 175 | let mediaUrl; 176 | for (let host of json.hosts) { 177 | const auth = encodeURIComponent(json.auth); // the auth token 178 | const url = `https://${host.hostname}${Constants_1.MediaPathMap[mediaType]}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`; 179 | try { 180 | const urlFetch = await this.fetchRequest(url, 'POST', body, options.uploadAgent, { 'Content-Type': 'application/octet-stream' }); 181 | const result = await urlFetch.json(); 182 | mediaUrl = result === null || result === void 0 ? void 0 : result.url; 183 | if (mediaUrl) 184 | break; 185 | else { 186 | json = await this.refreshMediaConn(true); 187 | throw new Error(`upload failed, reason: ${JSON.stringify(result)}`); 188 | } 189 | } 190 | catch (error) { 191 | const isLast = host.hostname === json.hosts[json.hosts.length - 1].hostname; 192 | this.logger.error(`Error in uploading to ${host.hostname}${isLast ? '' : ', retrying...'}`); 193 | } 194 | } 195 | if (!mediaUrl) 196 | throw new Error('Media upload failed on all hosts'); 197 | const message = { 198 | [mediaType]: Constants_1.MessageTypeProto[mediaType].fromObject({ 199 | url: mediaUrl, 200 | mediaKey: mediaKey, 201 | mimetype: options.mimetype, 202 | fileEncSha256: fileEncSha256, 203 | fileSha256: fileSha256, 204 | fileLength: buffer.length, 205 | seconds: options.duration, 206 | fileName: options.filename || 'file', 207 | gifPlayback: isGIF || undefined, 208 | caption: options.caption, 209 | ptt: options.ptt 210 | }) 211 | }; 212 | return Constants_1.WAMessageProto.Message.fromObject(message); // as WAMessageContent 213 | } 214 | /** prepares a WAMessage for sending from the given content & options */ 215 | prepareMessageFromContent(id, message, options) { 216 | if (!options.timestamp) 217 | options.timestamp = new Date(); // set timestamp to now 218 | if (typeof options.sendEphemeral === 'undefined') 219 | options.sendEphemeral = 'chat'; 220 | // prevent an annoying bug (WA doesn't accept sending messages with '@c.us') 221 | id = Utils_1.whatsappID(id); 222 | const key = Object.keys(message)[0]; 223 | const timestamp = Utils_1.unixTimestampSeconds(options.timestamp); 224 | const quoted = options.quoted; 225 | if (options.contextInfo) 226 | message[key].contextInfo = options.contextInfo; 227 | if (quoted) { 228 | const participant = quoted.key.fromMe ? this.user.jid : (quoted.participant || quoted.key.participant || quoted.key.remoteJid); 229 | message[key].contextInfo = message[key].contextInfo || {}; 230 | message[key].contextInfo.participant = participant; 231 | message[key].contextInfo.stanzaId = quoted.key.id; 232 | message[key].contextInfo.quotedMessage = quoted.message; 233 | // if a participant is quoted, then it must be a group 234 | // hence, remoteJid of group must also be entered 235 | if (quoted.key.participant) { 236 | message[key].contextInfo.remoteJid = quoted.key.remoteJid; 237 | } 238 | } 239 | if (options === null || options === void 0 ? void 0 : options.thumbnail) { 240 | message[key].jpegThumbnail = Buffer.from(options.thumbnail, 'base64'); 241 | } 242 | const chat = this.chats.get(id); 243 | if ( 244 | // if we want to send a disappearing message 245 | (((options === null || options === void 0 ? void 0 : options.sendEphemeral) === 'chat' && (chat === null || chat === void 0 ? void 0 : chat.ephemeral)) || 246 | (options === null || options === void 0 ? void 0 : options.sendEphemeral) === true) && 247 | // and it's not a protocol message -- delete, toggle disappear message 248 | key !== 'protocolMessage' && 249 | // already not converted to disappearing message 250 | key !== 'ephemeralMessage') { 251 | message[key].contextInfo = { 252 | ...(message[key].contextInfo || {}), 253 | expiration: (chat === null || chat === void 0 ? void 0 : chat.ephemeral) || Constants_1.WA_DEFAULT_EPHEMERAL, 254 | ephemeralSettingTimestamp: chat === null || chat === void 0 ? void 0 : chat.eph_setting_ts 255 | }; 256 | message = { 257 | ephemeralMessage: { 258 | message 259 | } 260 | }; 261 | } 262 | message = Constants_1.WAMessageProto.Message.fromObject(message); 263 | const messageJSON = { 264 | key: { 265 | remoteJid: id, 266 | fromMe: true, 267 | id: Utils_1.generateMessageID(), 268 | }, 269 | message: message, 270 | messageTimestamp: timestamp, 271 | messageStubParameters: [], 272 | participant: id.includes('@g.us') ? this.user.jid : null, 273 | status: Constants_1.WA_MESSAGE_STATUS_TYPE.PENDING 274 | }; 275 | return Constants_1.WAMessageProto.WebMessageInfo.fromObject(messageJSON); 276 | } 277 | /** Relay (send) a WAMessage; more advanced functionality to send a built WA Message, you may want to stick with sendMessage() */ 278 | async relayWAMessage(message, { waitForAck } = { waitForAck: true }) { 279 | var _a; 280 | const json = ['action', { epoch: this.msgCount.toString(), type: 'relay' }, [['message', null, message]]]; 281 | const flag = message.key.remoteJid === ((_a = this.user) === null || _a === void 0 ? void 0 : _a.jid) ? Constants_1.WAFlag.acknowledge : Constants_1.WAFlag.ignore; // acknowledge when sending message to oneself 282 | const mID = message.key.id; 283 | message.status = Constants_1.WA_MESSAGE_STATUS_TYPE.PENDING; 284 | const promise = this.query({ 285 | json, 286 | binaryTags: [Constants_1.WAMetric.message, flag], 287 | tag: mID, 288 | expect200: true 289 | }) 290 | .then(() => message.status = Constants_1.WA_MESSAGE_STATUS_TYPE.SERVER_ACK); 291 | if (waitForAck) { 292 | await promise; 293 | } 294 | else { 295 | promise 296 | .then(() => this.emit('message-status-update', { ids: [mID], to: message.key.remoteJid, type: Constants_1.WA_MESSAGE_STATUS_TYPE.SERVER_ACK })) 297 | .catch(() => this.emit('message-status-update', { ids: [mID], to: message.key.remoteJid, type: Constants_1.WA_MESSAGE_STATUS_TYPE.ERROR })); 298 | } 299 | await this.chatAddMessageAppropriate(message); 300 | } 301 | /** 302 | * Fetches the latest url & media key for the given message. 303 | * You may need to call this when the message is old & the content is deleted off of the WA servers 304 | * @param message 305 | */ 306 | async updateMediaMessage(message) { 307 | var _a, _b, _c, _d, _e; 308 | const content = ((_a = message.message) === null || _a === void 0 ? void 0 : _a.audioMessage) || ((_b = message.message) === null || _b === void 0 ? void 0 : _b.videoMessage) || ((_c = message.message) === null || _c === void 0 ? void 0 : _c.imageMessage) || ((_d = message.message) === null || _d === void 0 ? void 0 : _d.stickerMessage) || ((_e = message.message) === null || _e === void 0 ? void 0 : _e.documentMessage); 309 | if (!content) 310 | throw new Constants_1.BaileysError(`given message ${message.key.id} is not a media message`, message); 311 | const query = ['query', { type: 'media', index: message.key.id, owner: message.key.fromMe ? 'true' : 'false', jid: message.key.remoteJid, epoch: this.msgCount.toString() }, null]; 312 | const response = await this.query({ json: query, binaryTags: [Constants_1.WAMetric.queryMedia, Constants_1.WAFlag.ignore], expect200: true }); 313 | Object.keys(response[1]).forEach(key => content[key] = response[1][key]); // update message 314 | } 315 | /** 316 | * Securely downloads the media from the message. 317 | * Renews the download url automatically, if necessary. 318 | */ 319 | async downloadMediaMessage(message) { 320 | var _a, _b, _c, _d; 321 | let mContent = ((_b = (_a = message.message) === null || _a === void 0 ? void 0 : _a.ephemeralMessage) === null || _b === void 0 ? void 0 : _b.message) || message.message; 322 | if (!mContent) 323 | throw new Constants_1.BaileysError('No message present', { status: 400 }); 324 | try { 325 | const buff = await Utils_1.decodeMediaMessageBuffer(mContent, this.fetchRequest); 326 | return buff; 327 | } 328 | catch (error) { 329 | if (error instanceof Constants_1.BaileysError && error.status === 404) { // media needs to be updated 330 | this.logger.info(`updating media of message: ${message.key.id}`); 331 | await this.updateMediaMessage(message); 332 | mContent = ((_d = (_c = message.message) === null || _c === void 0 ? void 0 : _c.ephemeralMessage) === null || _d === void 0 ? void 0 : _d.message) || message.message; 333 | const buff = await Utils_1.decodeMediaMessageBuffer(mContent, this.fetchRequest); 334 | return buff; 335 | } 336 | throw error; 337 | } 338 | } 339 | /** 340 | * Securely downloads the media from the message and saves to a file. 341 | * Renews the download url automatically, if necessary. 342 | * @param message the media message you want to decode 343 | * @param filename the name of the file where the media will be saved 344 | * @param attachExtension should the parsed extension be applied automatically to the file 345 | */ 346 | async downloadAndSaveMediaMessage(message, filename, attachExtension = true) { 347 | const buffer = await this.downloadMediaMessage(message); 348 | const extension = Utils_1.extensionForMediaMessage(message.message); 349 | const trueFileName = attachExtension ? (filename + '.' + extension) : filename; 350 | await fs_1.promises.writeFile(trueFileName, buffer); 351 | return trueFileName; 352 | } 353 | /** Query a string to check if it has a url, if it does, return required extended text message */ 354 | async generateLinkPreview(text) { 355 | const query = ['query', { type: 'url', url: text, epoch: this.msgCount.toString() }, null]; 356 | const response = await this.query({ json: query, binaryTags: [26, Constants_1.WAFlag.ignore], expect200: true, requiresPhoneConnection: false }); 357 | if (response[1]) 358 | response[1].jpegThumbnail = response[2]; 359 | const data = response[1]; 360 | const content = { text }; 361 | content.canonicalUrl = data['canonical-url']; 362 | content.matchedText = data['matched-text']; 363 | content.jpegThumbnail = data.jpegThumbnail; 364 | content.description = data.description; 365 | content.title = data.title; 366 | content.previewType = 0; 367 | return content; 368 | } 369 | async refreshMediaConn(forceGet = false) { 370 | if (!this.mediaConn || forceGet || (new Date().getTime() - this.mediaConn.fetchDate.getTime()) > this.mediaConn.ttl * 1000) { 371 | this.mediaConn = await this.getNewMediaConn(); 372 | this.mediaConn.fetchDate = new Date(); 373 | } 374 | return this.mediaConn; 375 | } 376 | async getNewMediaConn() { 377 | const { media_conn } = await this.query({ json: ['query', 'mediaConn'], requiresPhoneConnection: false }); 378 | return media_conn; 379 | } 380 | } 381 | __decorate([ 382 | Mutex_1.Mutex(message => { var _a; return (_a = message === null || message === void 0 ? void 0 : message.key) === null || _a === void 0 ? void 0 : _a.id; }) 383 | ], WAConnection.prototype, "updateMediaMessage", null); 384 | __decorate([ 385 | Mutex_1.Mutex(message => { var _a; return (_a = message === null || message === void 0 ? void 0 : message.key) === null || _a === void 0 ? void 0 : _a.id; }) 386 | ], WAConnection.prototype, "downloadMediaMessage", null); 387 | __decorate([ 388 | Mutex_1.Mutex() 389 | ], WAConnection.prototype, "refreshMediaConn", null); 390 | exports.WAConnection = WAConnection; 391 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ephemeralMod 2 | Tinggal tempel aja cuk! auto bisa bug 3 | --------------------------------------------------------------------------------